In Android, How would one save a text file from the internet to the sdcard, load the file from the sdcard and separate the entries of the file by commas into a ListView?
It's important that more than one of the entries in the ListView can be selected at once.
The use of fragments would be nice too, as it would make it easier to use with different screen sizes such as phones vs. tablets.
I see your question as 2 different problems, each with their own solutions and obstacles, that come together in the end. My examples are all compiled against API16 (4.1 Jelly Bean) with a minimum of API11 (3.0 Honeycomb). - WARNING - large amounts of text incoming.
Load from Internet
Loading from the internet seems overwhelming at first, although it is simple in the end. First you want to ensure that the device has a connection. To do this, you create a method called getConnectivityStatus as seen below:
public boolean getConnectivityStatus() {
ConnectivityManager cm = (ConnectivityManager) this
.getSystemService(CONNECTIVITY_SERVICE);
NetworkInfo info = cm.getActiveNetworkInfo();
if (info != null)
return info.isConnected();
else
return false;
}
If a connection exists, you'll want to create a directory to save the file and download the file using the DownloadManager class. To do this, simply say:
File directory = new File(Environment.getExternalStorageDirectory(), "ExampleDirectory");
if (!directory.exists())
directory.mkdir();
Next, you'll want to download the file with the method downloadFile(String), passing in the file name you'll want. If you want only a single copy of the file at any given time, you'll have to delete the old file if it exists before downloading, or you will have multiple files such as examplefile.txt; examplefile-1.txt; examplefile-2.txt; Place this first portion of code in the method you want to start the download from, such as onClick:
String FILE_NAME = "examplefile.txt",
File examplefile = new File(Environment.getExternalStorageDirectory()
+ "/ExampleDirectory", FILE_NAME);
if (examplefile.exists()) {
boolean deleted = examplefile.delete();
if (deleted) {
if (getConnectivityStatus())
downloadFile(FILE_NAME);
}
}
The downloadFile(String) method:
public void downloadFile(String FILE_NAME) {
String url = "http://www.example.com/filetobedownloaded.txt";
DownloadManager.Request request = new DownloadManager.Request(
Uri.parse(url));
request.setDescription("Example file to be displayed.");
request.setTitle(FILE_NAME);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
request.allowScanningByMediaScanner();
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE);
}
request.setDestinationInExternalPublicDir("ExampleDirectory", FILE_NAME);
DownloadManager manager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
manager.enqueue(request);
}
You can also register a receiver to return a callback when the download is complete. To do so, simple register the receiver in the onCreate method like so: registerReceiver(onComplete, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE)); and place the below method in your class:
BroadcastReceiver onComplete = new BroadcastReceiver() {
public void onReceive(Context ctxt, Intent intent) {
if (!started) {
started = true;
// perform action upon completion
}
}
};
Here is the final DownloadFile.java class:
public class DownloadFile extends Activity {
boolean started = false;
String url = "http://www.example.com/filetobedownloaded.txt";
String FILE_NAME = "examplefile.txt",
File directory = new File(Environment.getExternalStorageDirectory(), "ExampleDirectory");
File examplefile = new File(Environment.getExternalStorageDirectory()
+ "/ExampleDirectory", FILE_NAME);
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_downloadfile);
registerReceiver(onComplete, new IntentFilter(
DownloadManager.ACTION_DOWNLOAD_COMPLETE));
check();
}
public void check {
if (!directory.exists())
directory.mkdir();
if (!getConnectivityStatus()) {
if (!started) {
started = true;
// perform action if no connection
}
}
if (examplefile.exists()) {
boolean deleted = examplefile.delete();
if (deleted && !started) {
if (getConnectivityStatus())
downloadFile(FILE_NAME);
}
}
}
public boolean getConnectivityStatus() {
ConnectivityManager cm = (ConnectivityManager) this
.getSystemService(CONNECTIVITY_SERVICE);
NetworkInfo info = cm.getActiveNetworkInfo();
if (info != null)
return info.isConnected();
else
return false;
}
public void downloadFile(String FILE_NAME) {
String url = "http://www.example.com/filetobedownloaded.txt";
DownloadManager.Request request = new DownloadManager.Request(
Uri.parse(url));
request.setDescription("Example file to be displayed.");
request.setTitle(FILE_NAME);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
request.allowScanningByMediaScanner();
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE);
}
request.setDestinationInExternalPublicDir("ExampleDirectory", FILE_NAME);
DownloadManager manager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
manager.enqueue(request);
}
BroadcastReceiver onComplete = new BroadcastReceiver() {
public void onReceive(Context ctxt, Intent intent) {
if (!started) {
started = true;
// perform action upon completion
}
}
};
Load into ListFragment
In order to load the file into a ListFragment, and later display the selected item, you'll have to create 3 classes and 2 xml files in the layout directory. In my example, I will use MainActivity.java , PreviewFragment.java , SelectionFragment.java , activity_main.xml , and fragment_preview.xml . We'll start with the xml. The first xml file is the one you are viewing, which contains the two fragments we are working with: the ListFragment and the PreviewFragment. The setup is fairly simple; you specify the two fragment, their ids and constraints, and their respective classes. Here is activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="horizontal" >
<fragment
android:id="#+id/fragmentSelection"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="2"
class="com.smarticle.catering.update.SelectionFragment" />
<fragment
android:id="#+id/fragmentPreview"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
class="com.smarticle.catering.update.PreviewFragment" />
</LinearLayout>
The above layout is optimized for a tablet in landscape mode. You can tweak the arrangement if you feel so inclined.
Next, you'll have to specify the PreviewFragment in xml, also fairly simple since it is only a TextView centered horizontally and vertically which will eventually display the selected items. Here is fragment_preview.xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<TextView
android:id="#+id/tvPreview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="#string/app_name"
android:textAppearance="?android:attr/textAppearanceLarge" />
</RelativeLayout>
The ListFragment will be created at runtime, so it will not need its own xml file.
In order to display the fragments on the screen, you'll have to load the activity_main.xml layout in your activity. This is a very simple task as it looks like every other activity, ever. This is MainActivity.java:
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
That's it, seriously. But now onward. In order to change the text in the PreviewFragment, you'll have to extend the Fragment class in PreviewFragment.java, inflate the view and setup a setText method. The PreviewFragment.java class is shown below:
public class PreviewFragment extends Fragment {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_preview, container,
false);
return view;
}
public void setText(String item) {
TextView tvPreview = (TextView) getView().findViewById(R.id.tvPreview);
tvPreview.setText(item);
}
}
The lifecycle for a fragment can be found here.
Now you'll have to setup the ListFragment. This will be done in the ListFragment.java class. In the onActivityCreated() method, you'll want to load the file, ensuring that it actually downloaded and is in the correct directory with the load(String) method. At this time, you'll also separate the file by its delimiter into an array. This is the load(String) method:
public void load(String FILE_NAME) {
String[] list;
String FILE_NAME = "examplefile.txt",
File directory = new File(Environment.getExternalStorageDirectory(), "ExampleDirectory");
File examplefile = new File(Environment.getExternalStorageDirectory()
+ "/ExampleDirectory", FILE_NAME);
if (examplefile.exists()) {
try {
File myFile = new File(directory + "/" + FILE_NAME);
FileInputStream fIn = new FileInputStream(myFile);
BufferedReader myReader = new BufferedReader(new InputStreamReader(
fIn));
String aDataRow = "";
String aBuffer = "";
while ((aDataRow = myReader.readLine()) != null) {
aBuffer += aDataRow;
aBuffer = aBuffer.trim();
list = aBuffer.split(",");
}
myReader.close();
if (!loaded)
Toast.makeText(getActivity(),
"Done reading '" + FILE_NAME + "'.", Toast.LENGTH_SHORT)
.show();
loaded = true;
if (!selections.equals("")) {
for (int i = 0; i < selections.size(); i++) {
getListView().setItemChecked(selections.get(i), true);
}
}
} catch (Exception e) {
Toast.makeText(getActivity(), e.getMessage(), Toast.LENGTH_SHORT)
.show();
}
}
}
This will return the String array list with the contents of examplefile.txt separated by commas. You can replace the commas with whatever delimiter you want, as long as the expression of aBuffer.split(String delimiter) is consistent with the delimiter in the text file. The boolean value loaded is only to make sure a new Toast doesn't appear everytime the activity is recreated, such as on an orientation change.
In the load(String) method, it's also a good time to set up your ListFragment's adapter and mode. You'll want to select a textViewResourceId that allows multiple choice, unless you want single choice. This can be done smoothly after the while statement by simple inserting these lines:
ArrayAdapter<String> adapter = new ArrayAdapter<String>(
getActivity(),
android.R.layout.simple_list_item_activated_1, list);
setListAdapter(adapter);
getListView().setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
If single choice is desired, change ListView.CHOICE_MODE_MULTIPLE to ListView.CHOICE_MODE_SINGLE and change android.R.layout.simple_list_item_activated_1 to android.R.layout.simple_list_item_1. Alternatively, if you want checks instead of highlighted, change to android.R.layout.simple_list_item_checked.
In this class, you must also initialize your PreviewFragment, done so in the onActivityCreated method like so: fragment = (PreviewFragment) getFragmentManager().findFragmentById(
R.id.fragmentPreview);
Lastly, you'll have to be able to read which items are selected in the ListFragment and display them on the PreviewFragment. This I do with a method named getSelectedItems() shown below:
public void getSelectedItems() {
cntChoice = getListView().getCount();
items = "";
selections.clear();
SparseBooleanArray sparseBooleanArray = getListView()
.getCheckedItemPositions();
for (int i = 0; i < cntChoice; i++) {
if (sparseBooleanArray.get(i) == true) {
items += getListView().getItemAtPosition(i).toString()
+ ";\n";
selections.add(i);
}
}
if (fragment != null && fragment.isInLayout())
fragment.setText(items);
}
The String items is what is displayed in the TextView, and selections is an ArrayList<Integer> used to restore the state upon an orientation change. Normally, you would specify an android:configChanges="orientation" in the AndroidManifest.xml file under the <activity > tag, but the problem occurs when using separate layouts for portrait or landscape. If you allow the Manifest to handle orientation changes, the layout is not changed when the orientation is changed because a new activity is not created like it would be under normal circumstances. Therefor, you create static ArrayList<Integer> containing the positions which contain a selected item.
The last thing to do is read when a ListItem has been clicked and to call the getSelectedItems method, a fairly simple task. Insert this below anywhere into your class:
#Override
public void onListItemClick(ListView l, View v, int position, long id) {
super.onListItemClick(l, v, position, id);
getItems();
}
You're finished! Now to put it all together. Here is the ListFragment.java class:
public class ListFragment extends ListFragment {
String FILE_NAME = "examplefile.txt", items = "";
String[] list;
static ArrayList<Integer> selections = new ArrayList<Integer>();
int cntChoice, position;
static boolean loaded = false;
File directory = new File(Environment.getExternalStorageDirectory(), "ExampleDirectory");
File examplefile = new File(Environment.getExternalStorageDirectory()
+ "/ExampleDirectory", FILE_NAME);
PreviewFragment fragment;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
fragment = (PreviewFragment) getFragmentManager().findFragmentById(
R.id.fragmentPreview);
check();
}
#Override
public void onListItemClick(ListView l, View v, int position, long id) {
super.onListItemClick(l, v, position, id);
getSelectedItems();
}
public void getSelectedItems() {
cntChoice = getListView().getCount();
items = "";
selections.clear();
SparseBooleanArray sparseBooleanArray = getListView()
.getCheckedItemPositions();
for (int i = 0; i < cntChoice; i++) {
if (sparseBooleanArray.get(i) == true) {
items += getListView().getItemAtPosition(i).toString()
+ ";\n";
selections.add(i);
}
}
if (fragment != null && fragment.isInLayout())
fragment.setText(items);
}
public void check() {
if (examplefile.exists())
load(FILE_NAME);
}
public void load(String FILE_NAME) {
try {
File myFile = new File(directory + "/" + FILE_NAME);
FileInputStream fIn = new FileInputStream(myFile);
BufferedReader myReader = new BufferedReader(new InputStreamReader(
fIn));
String aDataRow = "";
String aBuffer = "";
while ((aDataRow = myReader.readLine()) != null) {
aBuffer += aDataRow;
aBuffer = aBuffer.trim();
list = aBuffer.split(",");
}
ArrayAdapter<String> adapter = new ArrayAdapter<String>(
getActivity(),
android.R.layout.simple_list_item_activated_1, list);
setListAdapter(adapter);
getListView().setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
myReader.close();
if (!loaded)
Toast.makeText(getActivity(),
"Done reading '" + FILE_NAME + "'.", Toast.LENGTH_SHORT)
.show();
loaded = true;
if (!selections.equals("")) {
for (int i = 0; i < selections.size(); i++) {
getListView().setItemChecked(selections.get(i), true);
}
getSelectedItems();
}
} catch (Exception e) {
Toast.makeText(getActivity(), e.getMessage(), Toast.LENGTH_SHORT)
.show();
}
}
}
Conclusion
I hope this solved your problem. I know it's lengthy, but it's thorough, tested and works well. To start this, you can do an Intent intent = new Intent(getBaseContext(), MainActivity.class); startActivity(intent); finish(); in the onReceive method of the receiver in the DownloadFile.java class. I would also suggest placing those same lines of code in the check() method, specifically the portion that is called if there is no connection, in which case, it will load the file that was previously downloaded to the directory. Good luck, happy coding and always remember... 01101000011101000111010001110000011100110011101000101111001011110111011101110111011101110010111001111001011011110111010101110100011101010110001001100101001011100110001101101111011011010010111101110111011000010111010001100011011010000011111101110110001111010110100101110101011000100100101000101101010110000101001101001100001110010110011101101111
Related
I have an android app that writes files to a directory in internal storage, but it only does that correctly for certain API's. It works fine on my phone which uses Android 5.1 (API 22). And on the Nexus 5 emulator (API 21). However, in most tablets with API >=23 it can't get the directory. It writes the file in a separate activity and puts them in a listView for display in this activity. Whenever this activity loads, it doesn't get any files because the directory 'path' doesn't exist. Yes, I have all the read/write external storage permissions in the manifest. I've included the full code for the activity if anyone really needs to see it, but the part that matters here is only down to the last log tag.
public class ListResults extends Activity {
int data_block = 100;
ArrayList<String> arraylist;
String path_string = Environment.getExternalStorageDirectory().getAbsolutePath()+"/TrafficCounter";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.list_results);
ListView listView1 = (ListView) findViewById(R.id.list);
File path = new File(path_string);
path.mkdirs();
String dir = "" + path;
Log.d("TAG","Path exists: " + path.exists());
Log.d("TAG",String.valueOf(path.mkdirs()));
Log.d("TAG",path.toString());
File f = new File(dir);
String[] fileList = f.list();
Log.d("TAG", Arrays.deepToString(fileList));
//////////////////////////////Rest of Activity (Not Relevant)///////////////////////////////////
arraylist= new ArrayList<String>();
if(fileList!=null){
for(int i=0;i<fileList.length;i++)
{
arraylist.add(fileList[i]);
}}
Collections.sort(arraylist);
ArrayAdapter<String> adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, arraylist);
listView1.setAdapter(adapter);
listView1.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
final String filename = ((TextView) view).getText().toString();
//Creating dialog for choosing what to do with files
final Dialog dialog = new Dialog(ListResults.this);
dialog.setContentView(R.layout.file_options_menu);
dialog.show();
dialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
TextView nameDisplay = (TextView)dialog.findViewById(R.id.file_name);
nameDisplay.setText(filename);
//Defining all buttons within the dialog
Button viewButton = (Button)dialog.findViewById(R.id.view_button);
Button shareButton = (Button)dialog.findViewById(R.id.share_button);
Button deleteButton = (Button)dialog.findViewById(R.id.delete_button);
Button cancelButton = (Button)dialog.findViewById(R.id.cancel_button);
//View option
viewButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
File path = new File(path_string);
File file = new File(path + "/" + filename);
String[] loadText = Load(file);
String finalString = "";
for (int i = 0; i < loadText.length; i++) {
finalString += loadText[i] + System.getProperty("line.separator");
}
// Launching new Activity on selecting single List Item
Intent i = new Intent(getApplicationContext(), SingleListItem.class);
// sending data to new activity
i.putExtra("product", finalString);
i.putExtra("filename export", filename);
dialog.cancel();
startActivity(i);
finish();
}
});
//Upload option from within the dialog
shareButton.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View v){
File path = new File(path_string);
File file = new File(path + "/" + filename);
Uri uri = Uri.fromFile(file);
Intent sendIntent = new Intent(Intent.ACTION_SEND);
sendIntent.putExtra(Intent.EXTRA_STREAM, uri);
sendIntent.setType("text/plain");
startActivity(Intent.createChooser(sendIntent,
getResources().getText(R.string.chooser_text)));
dialog.cancel();
}
});
//Delete button function
deleteButton.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View v){
File path = new File(path_string);
File file = new File(path,filename);
file.delete();
Toast.makeText(getApplicationContext(), "File Deleted", Toast.LENGTH_SHORT).show();
dialog.cancel();
Intent intent = getIntent();
finish();
startActivity(intent);
}
});
//Cancel option from dialog
cancelButton.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View v){
dialog.cancel();
}
});
}
});
}
public static String[] Load(File file)
{
FileInputStream fis = null;
try
{
fis = new FileInputStream(file);
}
catch (FileNotFoundException e) {e.printStackTrace();}
InputStreamReader isr = new InputStreamReader(fis);
BufferedReader br = new BufferedReader(isr);
String test;
int anzahl=0;
try
{
while ((test=br.readLine()) != null)
{
anzahl++;
}
}
catch (IOException e) {e.printStackTrace();}
try
{
fis.getChannel().position(0);
}
catch (IOException e) {e.printStackTrace();}
String[] array = new String[anzahl];
String line;
int i = 0;
try
{
while((line=br.readLine())!=null)
{
array[i] = line;
i++;
}
}
catch (IOException e) {e.printStackTrace();}
return array;
}
public void onRestart(View view) {
Intent restart = new Intent(this, MainActivity.class);
startActivity(restart);
}
}
I included the log tags I get for each of the following tests. First one is whether or not the directory exists, second line is if mkdirs(); was used, third is the actual address, and fourth is the actual array of files in the directory.
For the Nexus 5 Emulator (API 21, works):
D/TAG: Path exists: true
D/TAG: false
D/TAG: /storage/sdcard/TrafficCounter
D/TAG: [test.txt]
For my actual phone (API 22, works):
D/TAG: Path exists: true
D/TAG: false
D/TAG: /storage/emulated/0/TrafficCounter
D/TAG: [Test.txt]
For Nexus 7 and Nexus S Emulators (API 23, doesn't work):
D/TAG: Path exists: false
D/TAG: false
D/TAG: /storage/emulated/0/TrafficCounter
D/TAG: null
Does anyone know how to properly get the directory for the higher API's? Or am I completely missing something else?
I am working on an app that allows user to select a file from external storage and send it using Android Beam.
Here is the FileBrowser Activity to select a file from a directory and return the file name and directory path back to main activity:
public class FileBrowser extends Activity {
private String root;
private String currentPath;
private ArrayList<String> targets;
private ArrayList<String> paths;
private File targetFile;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_file_browser);
getActionBar().setDisplayHomeAsUpEnabled(true);
root = "/";
currentPath = root;
targets = null;
paths = null;
targetFile = null;
showDir(currentPath);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_file_browser, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
NavUtils.navigateUpFromSameTask(this);
return true;
}
return super.onOptionsItemSelected(item);
}
public void selectDirectory(View view) {
File f = new File(currentPath);
targetFile = f;
//Return target File to activity
returnTarget();
}
public void setCurrentPathText(String message)
{
TextView fileTransferStatusText = (TextView) findViewById(R.id.current_path);
fileTransferStatusText.setText(message);
}
private void showDir(String targetDirectory){
setCurrentPathText("Current Directory: " + currentPath);
targets = new ArrayList<String>();
paths = new ArrayList<String>();
File f = new File(targetDirectory);
File[] directoryContents = f.listFiles();
if (!targetDirectory.equals(root))
{
targets.add(root);
paths.add(root);
targets.add("../");
paths.add(f.getParent());
}
for(File target: directoryContents)
{
paths.add(target.getPath());
if(target.isDirectory())
{
targets.add(target.getName() + "/");
}
else
{
targets.add(target.getName());
}
}
ListView fileBrowserListView = (ListView) findViewById(R.id.file_browser_listview);
ArrayAdapter<String> directoryData = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, targets);
fileBrowserListView.setAdapter(directoryData);
fileBrowserListView.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> arg0, View view, int pos,long id) {
File f = new File(paths.get(pos));
if(f.isFile())
{
targetFile = f;
returnTarget();
//Return target File to activity
}
else
{
//f must be a dir
if(f.canRead())
{
currentPath = paths.get(pos);
showDir(paths.get(pos));
}
}
}
});
}
public void returnTarget()
{
Intent returnIntent = new Intent();
returnIntent.putExtra("file", targetFile);
returnIntent.putExtra("path", currentPath);
setResult(RESULT_OK, returnIntent);
finish();
}
}
Here is the code for MainActivity where the file returned by FileBrowser Activity is send using android beam:
public class MainActivity extends Activity {
private NfcAdapter nfcAdapter;
public final int fileRequestID = 98;
String name;
String[] extension={".png",".docx",".jpeg",".pdf",".doc"};
ArrayList <String>supportedExtension=new ArrayList<String>(Arrays.asList(extension));
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
PackageManager pm = this.getPackageManager();
// Check whether NFC is available on device
if (!pm.hasSystemFeature(PackageManager.FEATURE_NFC)) {
// NFC is not available on the device.
Toast.makeText(this, "The device does not has NFC hardware.",
Toast.LENGTH_SHORT).show();
}
// Check whether device is running Android 4.1 or higher
else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
// Android Beam feature is not supported.
Toast.makeText(this, "Android Beam is not supported.",
Toast.LENGTH_SHORT).show();
}
else {
// NFC and Android Beam file transfer is supported.
Toast.makeText(this, "Android Beam is supported on your device.",
Toast.LENGTH_SHORT).show();
}
}
public void browseForFile(View view) {
Intent clientStartIntent = new Intent(this, FileBrowser.class);
startActivityForResult(clientStartIntent, fileRequestID);
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
//fileToSend
boolean filePathProvided;
File fileToSend;
if (resultCode == Activity.RESULT_OK && requestCode == fileRequestID) {
//Fetch result
File targetDir = (File) data.getExtras().get("file");
String path = (String)data.getExtras().get("path");
Log.i("Path=",path);
if(targetDir.isFile())
{
if(targetDir.canRead()) {
try{
String ext=targetDir.getName().substring(targetDir.getName().lastIndexOf("."));
if (supportedExtension.contains(ext)) {
fileToSend = targetDir;
filePathProvided = true;
setTargetFileStatus(targetDir.getName() + " selected for file transfer");
Button btn = (Button) findViewById(R.id.send);
btn.setVisibility(View.VISIBLE);
name = targetDir.getName();
}
else{
Toast.makeText(getApplicationContext(), "File with this extension cannot be printed",
Toast.LENGTH_LONG).show();
}
}catch (Exception e){e.printStackTrace();}
}
else
{
filePathProvided = false;
setTargetFileStatus("You do not have permission to read the file " + targetDir.getName());
}
}
else
{
filePathProvided = false;
setTargetFileStatus("You may not transfer a directory, please select a single file");
}
}
}
public void setTargetFileStatus(String message)
{
TextView targetFileStatus = (TextView) findViewById(R.id.selected_filename);
targetFileStatus.setText(message);
}
public void sendFile(View view) {
nfcAdapter = NfcAdapter.getDefaultAdapter(this);
// Check whether NFC is enabled on device
if(!nfcAdapter.isEnabled()){
Toast.makeText(this, "Please enable NFC.", Toast.LENGTH_SHORT).show();
startActivity(new Intent(Settings.ACTION_NFC_SETTINGS));
}
else if(!nfcAdapter.isNdefPushEnabled()) {
Toast.makeText(this, "Please enable Android Beam.",
Toast.LENGTH_SHORT).show();
startActivity(new Intent(Settings.ACTION_NFCSHARING_SETTINGS));
}
else {
Uri[] mFileUris = new Uri[1];
String fileName=name;
// Retrieve the path to the user's public pictures directory
File fileDirectory = Environment
.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
File fileToTransfer;
fileToTransfer = new File(fileDirectory, fileName);
fileToTransfer.setReadable(true, false);
mFileUris[0] = Uri.fromFile(fileToTransfer);
nfcAdapter.setBeamPushUris(mFileUris, this);
}
}
}
Now, as you can see in my MainActivity, I am setting my directory as Pictures.
File fileDirectory = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
My question is How can I dynamically change my directory every time based on the actual directory value obtained from FileBrowser Activity?
I have already went through the android documentation of How to send files using Android Beam, but didn't find it much useful for my problem. I also went through the android documentation of Environment, but couldn't understand much.
Any help regarding this will really be appreciated. Thanks in advance!
You already have the file selected in OnActivityResult method. Just change
mFileUris[0] = Uri.fromFile(fileToTransfer);
to
mFileUris[0] = Uri.fromFile(targetDir);
I have an application that saves data to a file called 'sensorLog.txt'. I am not sure where exactly this is stored but I know this is only accessible by the applicationand it is in the internal memory.
I want to be able to write a copy the current file to an external storage when I click on a button "export". I have pasted a small bit of my program, But i am not sure how to copy sensorLog.txt file to the external storage.
public class MainActivity extends Activity {
private static final String DEBUG_TAG = "MainActivity";
private Button buttonStartService;
private Button buttonStopService;
private Button buttonSettings;
private Button buttonExport;
private TextView textStatus;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Context context = getApplicationContext();
setContentView(R.layout.activity_main);
buttonStartService = (Button) findViewById(R.id.button_start_service);
buttonStopService = (Button) findViewById(R.id.button_stop_service);
buttonSettings = (Button) findViewById(R.id.button_settings);
buttonExport = (Button) findViewById(R.id.button_export);
textStatus = (TextView) findViewById(R.id.text_status);
buttonStartService.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
startSensorService();
}
});
buttonStopService.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
stopSensorService();
}
});
//export button listener
buttonExport.setOnClickListener(export_handler);
}
public void startSensorService() {
// Schedule
AlarmManager scheduler = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(getApplicationContext(), SensorService.class);
PendingIntent scheduledIntent = PendingIntent.getService(getApplicationContext(), 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
// 30 seconds
long interval = 30 * 1000;
scheduler.setInexactRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), interval, scheduledIntent);
Log.d(DEBUG_TAG, "Service started");
}
public void stopSensorService() {
// Cancel
AlarmManager scheduler = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(this, SensorService.class);
PendingIntent scheduledIntent = PendingIntent.getService(getApplicationContext(), 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
scheduler.cancel(scheduledIntent);
Log.d(DEBUG_TAG, "Service stopped");
}
View.OnClickListener export_handler = new View.OnClickListener() {
public void onClick(View v)
{
// Here is the part I am not sure what to do. I want to copy a file sensorLog.txt that has all my sensor information to sd card
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state))
{
Log.d(DEBUG_TAG, "SD card detected");
stopSensorService();
File file = new File(Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_DOCUMENTS),"SensorLog.txt");
// delete file from the internal storage once exported
context.deleteFile("SensorLog.txt");
startSensorService();
}
else
{
Log.d(DEBUG_TAG, "No external storage detected(cannot copy file)");
}
}
};
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
}
Part where I create sensorLog.txt***(I dont think it is necessary to read for this question but just in case someone needs it)*:
private class SensorServiceLoggerTask extends AsyncTask<SensorFrame, Void, Void> {
#Override
protected Void doInBackground(SensorFrame... frames) {
SensorFrame frame = frames[0];
BufferedWriter bufWr = null;
try {
File file = new File(getApplicationContext().getFilesDir(), "SensorLog.txt");
if (file.exists()) {
// Write to new file
bufWr = new BufferedWriter(new FileWriter(file, true));
} else {
file.createNewFile();
Log.d(DEBUG_TAG, "New log file created");
// Append to existing file
bufWr = new BufferedWriter(new FileWriter(file, false));
// Write header
bufWr.append(sensorHeader.toString());
}
// Write frame
bufWr.append(sensorFrame.toString());
bufWr.flush();
Log.d(DEBUG_TAG, "Added frame to log");
} catch (IOException ex) {
// TODO: useful error handling
} finally {
// Cleanup
if (bufWr != null) {
try {
bufWr.close();
} catch (IOException ex) {
// TODO: useful error handling
}
}
}
return null;
}
}
I also have 2 more queries:
Lets say I want to append some information at the top of the file just before moving it how can I do that?
My aim is to transfer the sensorLog.txt file from internal to external storage when the export button is pressed. delete or empty the internal sensorLog.txt file and then the same thing happens again if i press export again, then I would have to rename my file when I export it right? would there not be a name clash? How do I handle that? could I give a name dynamically?
Thank you.
EDIT: Some corrections
View.OnClickListener export_handler = new View.OnClickListener() {
public void onClick(View v)
{
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state))
{
Log.d(DEBUG_TAG, "SD card detected");
stopSensorService();
Log.d(DEBUG_TAG, "stopSensorService for file transfer");
//make the timestamp the file name
long TS = System.currentTimeMillis();
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append(TS);
stringBuilder.append(".txt");
String file_name = stringBuilder.toString();
//file name stored in file_name
File file_ext = new File(Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_DOCUMENTS),file_name);
// attempt to create this new directory
//read from sensorLog.txt file
try
{
file_ext.createNewFile();
File file = getBaseContext().getFileStreamPath("sensorLog.txt");
if(file.exists())
{
FileInputStream read_file = openFileInput("sensorLog.txt");
//read contents of internal file
InputStreamReader inputStreamReader = new InputStreamReader(read_file);
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
StringBuilder sb = new StringBuilder();
sb.append("Timestamp of export to SD"+TS+"/n");
String line;
while ((line = bufferedReader.readLine()) != null) {
sb.append(line);
}
BufferedWriter bufWr = null;
bufWr = new BufferedWriter(new FileWriter(file_ext, false));
// Write header
bufWr.append(sb.toString());
inputStreamReader.close();
bufWr.close();
read_file.close();
//delete sensor file once exported
getApplicationContext().deleteFile("sensorLog.txt");
}
}
catch(Exception e){}
But for some reason my file is not getting stored in the SD card.
Check out the Android documentation. If you can read your sensorLog.txt file, then you can save it in a String and then save the string to a file in the external storage.
I'm writting an app, which lets user to input URL then select the location to put the downloaded file. But I'm get stuck in the 2nd step.
All I want to do is display the location like this:
And return the path to location.
Anyone has the solution? Thanks in advance!
PS: Display the external storage.
Create activity
public class ... extend activity
{
string path = "/";
public void onResume()
{
...
setContentView(..);
if (getIntent().hasExtra("path"))
{
path = getIntent().getStringExtra("path");
}
listview = findviewbyid(R.id.listview);
listview.setAdapter(new adapter(path));
listview.setOnItemClickListener(this);
}
public void onActivityResult(result)
{
if (resultOK) ...
}
public void onclicklisteneer(view,pos,id)
{
if (dir)
{
Intent intent = new Intent(this, this.class);
intent.put("path",path+" "+view.getAdapter().getItem(pos))
intent.setFlag(FLAG_NEW_TASK)
startActivityForResult(intent)
}
else if (file)
{
setResult(Result_OK);
finish();
}
}
}
This is how I do, and it works for me:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_tree_view);
File dir = new File("/sdcard");
ArrayList<String> folders = new ArrayList<String>();
final File[] fileList = dir.listFiles();
if (fileList == null){
Toast msg = Toast.makeText(this, "No files", 3000);
msg.show();
}else{
for (File f:fileList){
if (f.isDirectory()){
folders.add(f.getName());
}
}
final ListView lvFolder = (ListView)findViewById(R.id.lvTree);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_selectable_list_item, folders);
lvFolder.setAdapter(adapter);
lvFolder.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> arg0, View v,
int position, long id) {
// TODO Auto-generated method stub
Object o = lvFolder.getItemAtPosition(position);
String fullObject = (String)o;
Toast.makeText(getApplicationContext(), "You have chosen: " + " " + fullObject.toString(), Toast.LENGTH_LONG).show();
}
});
}
}
I am using IntentService to download 200 large JPGs from a list. While this is loading, the user can skip through the not-loaded JPGs and load JPG #156 for example, but after it is loaded, it should continue loading the rest. So it's like a Lazy Loader... but it continues when it's idle.
I previously used onHandleIntent and put a loop from #1 to #200... which obviously doesn't work when I try to send another IntentService call for JPG #156. So the call to #156 only happens after onHandleIntent is done with #200.
I then changed it so onHandleIntent reorders request #156 to be at the top of the list, then requests the top of the list (and downloads the JPG), then removes it from the list. It then calls the IntentService again, which sounds rather risky from a recursive/stack overflow kinda way. It works sometimes and I can see file #156 being put first... sometimes.
Is there a better way to do this? A way I could think of would be to run it all through a database.
EDIT: This is what I have come up with:
code
public class PBQDownloader extends IntentService {
int currentWeight = 0;
PriorityBlockingQueue<WeightedAsset> pbQueue = new PriorityBlockingQueue<WeightedAsset>(100, new CompareWeightedAsset());
public PBQDownloader() {
super("PBQDownloader");
}
public PBQDownloader(String name) {
super(name);
}
#Override
protected void onHandleIntent(Intent intent) {
String downloadUrl = "-NULL-";
Bundle extras = intent.getExtras();
if (extras!=null) {
downloadUrl = extras.getString("url");
Log.d("onHandleIntent 1.1", "asked to download: " + downloadUrl);
} else {
Log.d("onHandleIntent 1.2", "no URL sent so let's start queueing everything");
int MAX = 10;
for (int i = 1; i <= MAX; i++) {
// should read URLs from list
WeightedAsset waToAdd = new WeightedAsset("url: " + i, MAX - i);
if (pbQueue.contains(waToAdd)) {
Log.d("onStartCommand 1", downloadUrl + " already exists, so we are removing it and adding it back with a new priority");
pbQueue.remove(waToAdd);
}
pbQueue.put(waToAdd);
}
currentWeight = MAX + 1;
}
while (!pbQueue.isEmpty()) {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
}
WeightedAsset waToProcess = pbQueue.poll();
Log.d("onHandleIntent 2 DOWNLOADED", waToProcess.url);
}
Log.d("onHandleIntent 99", "finished all IntentService calls");
}
#Override
public int onStartCommand(Intent intent, int a, int b) {
super.onStartCommand(intent, a, b);
currentWeight++;
String downloadUrl = "-NULL-";
Bundle extras = intent.getExtras();
if (extras!=null) downloadUrl = extras.getString("url");
Log.d("onStartCommand 0", "download: " + downloadUrl + " with current weight: " + currentWeight);
WeightedAsset waToAdd = new WeightedAsset(downloadUrl, currentWeight);
if (pbQueue.contains(waToAdd)) {
Log.d("onStartCommand 1", downloadUrl + " already exists, so we are removing it and adding it back with a new priority");
pbQueue.remove(waToAdd);
}
pbQueue.put(waToAdd);
return 0;
}
private class CompareWeightedAsset implements Comparator<WeightedAsset> {
#Override
public int compare(WeightedAsset a, WeightedAsset b) {
if (a.weight < b.weight) return 1;
if (a.weight > b.weight) return -1;
return 0;
}
}
private class WeightedAsset {
String url;
int weight;
public WeightedAsset(String u, int w) {
url = u;
weight = w;
}
}
}
code
Then I have this Activity:
code
public class HelloPBQ extends Activity {
int sCount = 10;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Button tv01 = (Button) findViewById(R.id.tv01);
Button tv02 = (Button) findViewById(R.id.tv02);
Button tv03 = (Button) findViewById(R.id.tv03);
tv01.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
doPBQ();
}
});
tv02.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
doInitPBQ();
}
});
tv03.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
sCount = 0;
}
});
}
private void doInitPBQ() {
Intent intent = new Intent(getApplicationContext(), PBQDownloader.class);
//intent.putExtra("url", "url: " + sCount);
startService(intent);
}
private void doPBQ() {
sCount++;
Intent intent = new Intent(getApplicationContext(), PBQDownloader.class);
intent.putExtra("url", "url: " + sCount);
startService(intent);
}
}
code
Now the messy bit is that I have to keep an ever-increasing counter that runs the risk of going out of int bounds (WeightedAsset.weight) - is there a way to programmatically add to the queue and have it automatically be the head of the queue? I tried to replace WeightedAsset with a String, but it didn't poll() as I wanted, as a FIFO instead of a LIFO stack.
Here's how I'd try it first:
Step #1: Have the IntentService hold onto a PriorityBlockingQueue.
Step #2: Have onHandleIntent() iterate over the PriorityBlockingQueue, downloading each file in turn as it gets popped off the queue.
Step #3: Have onStartCommand() see if the command is the "kick off all downloads" command (in which case, chain to the superclass). If, instead, it's the "prioritize this download" command, re-prioritize that entry in the PriorityBlockingQueue, so it'll be picked up next by onHandleIntent() when the current download is finishing.