I am creating a class witch loads up a few sounds. However, isPlaying keeps on throwing an exception after a while and then stops playing that particular sound permanently, while other sounds keep playing OK.
public class MySound {
int m_IdMyId;
int m_ResId;
boolean m_IsLoaded;
MediaPlayer m_Media;
public MySound(int idMyId, int resId){
m_IdMyId = idMyId;
m_ResId = resId;
m_IsLoaded = false;
m_Media = null;
}
}
In this m_IdMyId is just an id for my game. m_ResId is something like R.raw.mysound1. m_IsLoaded I think is automatically set to true as I am loading synconously. m_Media is the MediaPlayer object.
I am calling stop() very regularly, as it is a game and I need to check every second or so to make sure certain sounds are stopped. It is here that it throws an exception when snd.m_Media.isPlaying() is called.
I cannot seem to access e to see what the error is.
Also I would like to know how I can set m_IsLoaded correctly. How do I know when the sound is fully loaded and ready to use?
Here is my management class:
public class MySoundManager {
MainActivity m_Context;
ArrayList<MySound> mySounds;
public MySoundManager(MainActivity context) {
m_Context = context;
mySounds = new ArrayList<MySound>();
mySounds.add(new MySound(8, R.raw.mysound1));
mySounds.add(new MySound(10, R.raw.mysound2));
mySounds.add(new MySound(22, R.raw.mysound3));
mySounds.add(new MySound(100, R.raw.click));
mySounds.add(new MySound(101, R.raw.error));
for(MySound mysound : mySounds) {
mysound.m_Media = MediaPlayer.create(m_Context, mysound.m_ResId); // no need to call prepare(); create() does that for you
mysound.m_IsLoaded = true;
}
}
// I call this when the main thread calls onResume
public void onResume(){
for(MySound mysound : mySounds) {
if(mysound.m_Media == null) {
mysound.m_Media = MediaPlayer.create(m_Context, mysound.m_ResId); // no need to call prepare(); create() does that for you
mysound.m_IsLoaded = true;
}
}
}
// I call this when the main thread calls onPause
public void onPause(){
for(MySound mysound : mySounds) {
if(mysound.m_Media != null) {
mysound.m_Media.stop();
mysound.m_Media.release();
mysound.m_Media = null;
}
}
}
public boolean IsAllLoaded(){
for(MySound mysound : mySounds) {
if(!mysound.m_IsLoaded) return false;
}
return true;
}
public MySound FindMySoundByIdMyId(int idMyId){
try {
for(MySound mysound : mySounds) {
if (mysound.m_IdMyId == idMyId) return mysound;
}
}catch(Exception e) {
MySound snd;
snd = null; // ToDo
}
return null;
}
public void play(int idMyId){
MySound snd;
try{
if((snd = FindMySoundByIdMyId(idMyId)) != null)
snd.m_Media.start();
}catch(IllegalStateException e) {
snd = null; // ToDo
}
}
public void pause(int idMyId){
MySound snd;
try{
if((snd = FindMySoundByIdMyId(idMyId)) != null &&
snd.m_Media.isPlaying())
snd.m_Media.pause();
}catch(IllegalStateException e) {
snd = null; // ToDo
}
}
public void pauseAll(){
try{
for (MySound mysound : mySounds) {
if(mysound.m_Media.isPlaying())
mysound.m_Media.pause();
}
}catch(IllegalStateException e) {
MySound snd;
snd = null; // ToDo
}
}
public boolean isPlaying(int idMyId, MySound[] fill){
MySound snd;
fill[0] = null;
try{
if((snd = FindMySoundByIdMyId(idMyId)) != null){
fill[0] = snd;
return snd.m_Media.isPlaying();
}
}catch(IllegalStateException e) {
snd = null; // ToDo
}
return false;
}
public void stop(int idMyId){
MySound snd;
try{
if((snd = FindMySoundByIdMyId(idMyId)) != null &&
snd.m_Media.isPlaying())
snd.m_Media.stop();
}catch(IllegalStateException e) {
snd = null; // ToDo
}
}
// The str is in the format
// number id, 1 = on 0 = off,dont play if this id playing;
public void PlaySound(String str) {
boolean isplaying;
int i, len, id, idDontPlay, milliNow;
String[] strARR = str.split(";");
String[] strARR2;
Integer[] tmpIntARR;
ArrayList<Integer[]> onARR = new ArrayList<Integer[]>();
ArrayList<Integer> offARR = new ArrayList<Integer>();
MySound snd;
for (i = 0, len = strARR.length; i < len; i++) {
if(strARR[i].length() <= 0) continue;
if((strARR2 = strARR[i].split(",")) != null &&
strARR2.length >= 3 &&
strARR2[0].length() > 0 &&
strARR2[1].length() > 0 &&
strARR2[2].length() > 0){
id = Integer.parseInt(strARR2[0]);
idDontPlay = Integer.parseInt(strARR2[2]);
tmpIntARR = new Integer[2];
tmpIntARR[0] = id;
tmpIntARR[1] = idDontPlay;
if(Integer.parseInt(strARR2[1]) == 1){
onARR.add(tmpIntARR);
} else offARR.add(id);
}
}
// Turn off all sounds that need to be turned off
for (i=0,len=offARR.size();i<len;i++) {
id = offARR.get(i);
stop(id);
}
// Turn all sounds that need to be turned on,
// but only if the sound that blocks a new sound is not playing
for (i=0,len=onARR.size();i<len;i++) {
tmpIntARR = onARR.get(i);
id = tmpIntARR[0];
idDontPlay = tmpIntARR[1];
// We dont play if the idDontPlay sound is already playing
if((snd = FindMySoundByIdMyId(idDontPlay)) != null &&
snd.m_Media.isPlaying())
continue;
if((snd = FindMySoundByIdMyId(id)) != null){
isplaying = snd.m_Media.isPlaying();
milliNow = snd.m_Media.getCurrentPosition();
if(milliNow > (snd.m_Media.getDuration() - 1000) ||
(!isplaying && milliNow > 0)){
snd.m_Media.seekTo(0); // Half a second inside
}
if(!isplaying) snd.m_Media.start();
}
}
}
}
Creating a MediaPlayer instance for every sound is not a good practice to get low latency, especially for short clips. MediaPlayer is for longer clips such as Music files it uses large buffer so, larger buffer means high latency. Also, there is AudioFocus mechanism on Android that may interfere your sound playing session. So, I strongly recommend you to use SoundPool to play short clips like game sounds.
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 want to check if an SD card is inserted in an Android device (currently a Minix Neo X7).
If I use:
String state = Environment.getExternalStorageState();
if( Environment.MEDIA_MOUNTED.equals( state ) ) { }
I get always true.
And also "Environment.isExternalStorageRemovable()" returns always true.
Any idea?
You can check if external removable sd card is available like this
public static boolean externalMemoryAvailable(Activity context) {
File[] storages = ContextCompat.getExternalFilesDirs(context, null);
if (storages.length > 1 && storages[0] != null && storages[1] != null)
return true;
else
return false;
}
This works perfectly.
You can detect an insertable (not emulated) sdcard or a usb storage connected to device with below code.
public void CHECK_EXTERNAL_STORAGE () {
File storage_directory = new File("/storage");
File storage;
for (int i = 0; i < storage_directory.listFiles().length; i++) {
storage = storage_directory.listFiles()[i];
if (storage.getAbsolutePath().contains("emulated")) {
continue;
}
if (storage.getAbsolutePath().equals("/storage/self")) {
continue;
}
if (Environment.getExternalStorageState(storage).equals(Environment.MEDIA_MOUNTED)) {
if (Environment.isExternalStorageEmulated(storage) == false) {
final File finalStorage = storage;
runOnUiThread(new Runnable() {
#Override
public void run() {
Log.d(TAG, "External SD Card exists. Path: " + finalStorage.getAbsolutePath());
text_view2.setText("External Storage exists. Path: " + finalStorage.getAbsolutePath());
}
});
}
}
else {
Log.d(TAG, "No external Storage detected.");
}
}
}
A little long winded but...
if(Environment.MEDIA_UNMOUNTABLE.equals(false) &&
Environment.MEDIA_UNKNOWN.equals( false ))
{
if(Environment.MEDIA_REMOVED.equals( false) &&
Environment.MEDIA_UNMOUNTED.equals( false ))
{ //mounted }
}
I am working on a application which takes a user's mobile number and stores the number in a file locally and stores it in a cloud database as well . I have written application logic in such a way that if the user exits from the app it maintains the state. When user opens the App again, the home page is opened with the same mobile number. I want to clear the all the data on Logout button click, and then again want to open the MobileHarvestMainActivity.java file (ask to enter the number again from user).
I tried the link How to programmatically clear application data — it worked but when using the intent to send the MainActivity page App crashes.
logout.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
v.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP);
Toast.makeText(getBaseContext(), "clicked", Toast.LENGTH_SHORT).show();
Intent intent = new Intent(ListeningAndSharingHomeActivity.this,MyActivity.class);
// intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); // To clean up all activities
startActivity(intent);
finish();
}
});
}
public class MyActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//setContentView(R.layout.activity_my);
clearApplicationData();
}
public void clearApplicationData() {
File cache = getCacheDir();
File appDir = new File(cache.getParent());
if (appDir.exists()) {
String[] children = appDir.list();
for (String s : children) {
if (!s.equals("lib")) {
deleteDir(new File(appDir, s));
Log.i("TAG", "**************** File /data/data/APP_PACKAGE/" + s + " DELETED *******************");
}
}
}
calling();
}
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;
}
}
}
return dir.delete();
}
void calling()
{
Intent intent = new Intent(getApplicationContext(), MobileHarvestMainActivity.class);
//intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
}
}
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