Listview from arrayadapter filled text file always one update behind - android-arrayadapter

I have a listview that I want to update with information from a textfile (rollcall.txt). Each time rollcall.txt is updated I am calling rollcall() (code below). The data is updated correctly in the text file before rollcall() is called, I have checked. The problem I have is that the listview doesnt show the updated entry until the next time I call rollcall() (I.E it always appears to be one update step behind).
Where am I going wrong?
public void rollcall(){
String[] splitdata = null;
try{
File myFile = new File(Environment.getExternalStorageDirectory() + "/rollcall.txt");
FileInputStream fIn = new FileInputStream(myFile);
BufferedReader myReader = new BufferedReader(
new InputStreamReader(fIn));
String aDataRow = "";
String aBuffer = "";
while ((aDataRow = myReader.readLine()) != null) {
aBuffer += aDataRow + "\n";
}
splitdata = aBuffer.split("`"); //recover the file and split it based on `
myReader.close();
}
catch (Exception e) {
Toast.makeText(getBaseContext(), e.getMessage(),
Toast.LENGTH_SHORT).show();
}
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, R.layout.logbooklayout, splitdata);
lv1.setAdapter(adapter);
adapter.notifyDataSetChanged(); //called to ensure updated data is refreshed into listview without reload
EDIT: rollcall is called from this method:
public void onClick(View v) {
if (v==badd){
AlertDialog.Builder alert = new AlertDialog.Builder(this);
alert.setTitle("ROLLCALL"); //Set Alert dialog title here
alert.setMessage("Enter data: "); //Message here
// Set an EditText view to get user input
final EditText input = new EditText(this);
alert.setView(input);
alert.setPositiveButton("OK", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
//You will get as string input data in this variable.
// here we convert the input to a string and show in a toast.
add = input.getEditableText().toString();
try {
File myFile = new File(Environment.getExternalStorageDirectory() + "/rollcall.txt");
myFile.createNewFile();
FileOutputStream fOut = new FileOutputStream(myFile, true);
OutputStreamWriter myOutWriter = new OutputStreamWriter(fOut);
myOutWriter.append(add);
myOutWriter.append("`"); // ` used to split the file down later in lv section
myOutWriter.close();
fOut.close();
}
catch (Exception e) {
Toast.makeText(getBaseContext(), e.getMessage(),
Toast.LENGTH_SHORT).show();
}
} // End of onClick(DialogInterface dialog, int whichButton)
}); //End of alert.setPositiveButton
alert.setNegativeButton("CANCEL", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
// Canceled.
dialog.cancel();
}
}); //End of alert.setNegativeButton
AlertDialog alertDialog = alert.create();
alertDialog.show();
rollcall();
}//end badd
}
Thanks for the help, I am new to using arrayadapters.
Andy

Short answer to your question is everything in UI thread is asynchronous and unless you somehow manage to freeze/lock the whole application you can't make the rest of your UI wait for your alert to grab the input. So long before you press "OK" button in your alert, your rollcall() method is being called from your onClick() function and whatever is inside your .txt file is being read/displayed on your UI, right behind your alert dialog hanging on for you to press one of the buttons, asynchronously.
Maybe the fastest solution to what you want to achieve is to call your rollcall() function somewhere else, after you confirm that your adapter's feeding data has actually been changed. If you must call it from within onClick() function, without questioning your reasons to do so, you should call it inside the try{} block, right after you close the output stream.
Like this:
try {
File myFile = new File(Environment
.getExternalStorageDirectory() + "/rollcall.txt");
myFile.createNewFile();
FileOutputStream fOut = new FileOutputStream(myFile, true);
OutputStreamWriter myOutWriter = new OutputStreamWriter(
fOut);
myOutWriter.append(add);
myOutWriter.append("`"); // ` used to split the
// file down later
// in lv section
myOutWriter.close();
fOut.close();
rollcall();
}
The reason this "works" is you already declared the listener for your "OK" button and whenever you press it, whatever inside your EditText input will be written on file. In order to make it work as before I think you need superhuman skills to write some text on alert dialog and click on button before rollcall() function is called in the same scope.
Obviously the better way to do update the list view is to be able to use adapter.notifyDataSetChanged() but I believe you should call it somewhere else than where you write on your file and in that case your adapter must be declared outside the scope of rollcall() function.
Anyways in order to show how it all goes on I created a simple(rather ugly) android application and put some logs on where the mysterious stuff is happening:
public class MainActivity extends Activity {
private ListView lv1;
private Button refreshButton;
ArrayAdapter<String> adapter;
String[] splitdata;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
refreshButton = (Button) findViewById(R.id.refreshButton);
lv1 = (ListView) findViewById(R.id.someTextViewId);
refreshButton.setOnClickListener(myButtonhandler);
splitdata = null;
}
View.OnClickListener myButtonhandler = new View.OnClickListener() {
public void onClick(View v) {
Log.d("main", "la noliy");
someFunction();
}
};
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
public void someFunction() {
AlertDialog.Builder alert = new AlertDialog.Builder(this);
alert.setTitle("ROLLCALL"); // Set Alert dialog title here
alert.setMessage("Enter data: "); // Message here
// Set an EditText view to get user input
final EditText input = new EditText(this);
alert.setView(input);
alert.setPositiveButton("OK", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
// You will get as string input data in this
// variable.
// here we convert the input to a string and show in
// a toast.
String add = input.getEditableText().toString();
try {
File myFile = new File(Environment
.getExternalStorageDirectory() + "/rollcall.txt");
myFile.createNewFile();
FileOutputStream fOut = new FileOutputStream(myFile, true);
OutputStreamWriter myOutWriter = new OutputStreamWriter(
fOut);
myOutWriter.append(add);
myOutWriter.append("`"); // ` used to split the
// file down later
// in lv section
myOutWriter.close();
fOut.close();
if (splitdata.length > 0) {
rollcall(new String("call from inside"));
}
} catch (Exception e) {
Toast.makeText(getBaseContext(), e.getMessage(),
Toast.LENGTH_SHORT).show();
}
} // End of onClick(DialogInterface dialog, int
// whichButton)
}); // End of alert.setPositiveButton
alert.setNegativeButton("CANCEL",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
// Canceled.
dialog.cancel();
}
}); // End of alert.setNegativeButton
AlertDialog alertDialog = alert.create();
alertDialog.show();
Log.d("someFunction", "before rollcall");
Log.d("someFunction", "location: "
+ Environment.getExternalStorageDirectory().getAbsolutePath());
rollcall(new String("call from outside"));
Log.d("someFunction", "after rollcall");
}// end badd
public void rollcall(String message) {
Log.d("rollcall", message);
try {
File myFile = new File(Environment.getExternalStorageDirectory()
+ "/rollcall.txt");
FileInputStream fIn = new FileInputStream(myFile);
BufferedReader myReader = new BufferedReader(new InputStreamReader(
fIn));
String aDataRow = "";
String aBuffer = "";
while ((aDataRow = myReader.readLine()) != null) {
aBuffer += aDataRow + "\n";
}
splitdata = aBuffer.split("`"); // recover the file and split it
// based on `
myReader.close();
} catch (Exception e) {
Toast.makeText(getBaseContext(), e.getMessage(), Toast.LENGTH_SHORT)
.show();
}
int length = splitdata.length;
for (int i = 0; i < length; i++) {
Log.d("rollcall", splitdata[i]);
}
adapter = new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1, splitdata);
lv1.setAdapter(adapter);
}
}
I put a button and an onClickListener to it. The first time you press the button everything is called, listview is updated and your dialog is hanging on your screen for either of the buttons to be pressed:
And you will see a log like this:
07-26 04:09:20.802: D/someFunction(11273): before rollcall
07-26 04:09:20.802: D/someFunction(11273): location: /mnt/sdcard
07-26 04:09:20.802: D/rollcall(11273): call from outside
07-26 04:09:20.802: D/rollcall(11273): some data
07-26 04:09:20.802: D/rollcall(11273): some other data
07-26 04:09:20.812: D/someFunction(11273): after rollcall
You can see that rollcall() has been called from outside and not inside of your try/catch block since there is also another call from there to rollcall(). But when you press the button your try/catch block will do it's job inside your onClick() function and rollcall() will be called afterwards. Hence your listview wil be updated with new data you just entered in the dialog:
Here is the final part of log right after you press "OK" you can see that rollcall() is being called and it can read the new data:
07-26 04:09:46.347: D/rollcall(11273): call from inside
07-26 04:09:46.357: D/rollcall(11273): some data
07-26 04:09:46.357: D/rollcall(11273): some other data
07-26 04:09:46.357: D/rollcall(11273): new data
Finally, I'm sure there are a lot of ugliness in this whole approach to your problem. Bottom line is you need to know that everything happening in the UI thread is asynchronous and no one is waiting for you to enter data inside your dialog in that onClick() function. You should update your listview somewhere else with a more elegant approach in case your application throws an exception for example around that try/catch block. At least maybe you should add a finally{} block at the end of it and update your listview in there even though the try part fails. Hope this answered your question:)
PS. For those who want to try this at home, remember to provide a TextView id from your layout.xml file to the findViewById() function to get the ListView reference in your code, not an actual ListView id. Yeah, I know...

call adapter.notifyDataSetChanged() everytime you update your adapter, then listview will automatically be updated

I suggest you run rollcall as an asychronous task for 2 reasons. First, it will not stop your UI when rollcall() is running.
Second, you will be able to call onPostExecute(Object o) wher you can call `adapter.notifyDataSetChanged(); '

Related

Why won't my Xamarin forms custom editor renderer do anything

I am trying to make a custom renderer for an editor that changes the "return" key to a "done" button and fires the Completed event when you tap it instead of typing a newline. The code in OnElementChanged() is being hit, but it's not doing anything. The "return" key is still a "return" key and it still types newlines instead of making the editor go out of focus. What am I doing wrong?
Here is the class for the custom editor (in the .NET project):
using Xamarin.Forms;
namespace Partylist.Custom_Controls
{
public class ChecklistEditor : Editor
{
}
}
Here is the custom renderer for Android:
using Android.Content;
using Android.Runtime;
using Android.Views;
using Android.Views.InputMethods;
using Android.Widget;
using Partylist.Custom_Controls;
using Partylist.Droid.Custom_Renderers;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
[assembly: ExportRenderer(typeof(ChecklistEditor), typeof(ChecklistEditorRenderer))]
namespace Partylist.Droid.Custom_Renderers
{
class ChecklistEditorRenderer : EditorRenderer
{
// Constructor because it needs to exist.
public ChecklistEditorRenderer(Context context) : base(context)
{
}
// This gets overridden so I can change what I want to change.
protected override void OnElementChanged(ElementChangedEventArgs
<Editor> e)
{
// Make it do what is should normally do so it will exist.
base.OnElementChanged(e);
// Make the "Return" button on the keyboard be a "Done" button.
Control.ImeOptions = ImeAction.Done;
// Make the thing watch for when the user hits the "Return" button.
Control.EditorAction += OnEditorAction;
}
// This makes the "Return" button fire the "Completed" event
// instead of typing a newline.
private void OnEditorAction(object sender, TextView
.EditorActionEventArgs e)
{
e.Handled = false;
if (e.ActionId == ImeAction.Done)
{
Control.ClearFocus();
e.Handled = true;
}
}
}
}
Here is the custom renderer for iOS:
using Foundation;
using Partylist.Custom_Controls;
using Partylist.iOS.Custom_Renderers;
using UIKit;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
[assembly: ExportRenderer(typeof(ChecklistEditor), typeof(ChecklistEditorRenderer))]
namespace Partylist.iOS.Custom_Renderers
{
class ChecklistEditorRenderer : EditorRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs
<Editor> e)
{
base.OnElementChanged(e);
Control.ReturnKeyType = UIReturnKeyType.Done;
}
protected override bool ShouldChangeText(UITextView textView,
NSRange range, string text)
{
if (text == "\n")
{
textView.ResignFirstResponder();
return false;
}
return true;
}
}
}
The code-behind for the page where I'm using these custom renderers (there's nothing in the XAML that should conflict with it, I think, but I'll add it to the post if people want to make sure):
using Partylist.Custom_Controls;
using System;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace Partylist.Views
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class ChecklistPage : ContentPage
{
// Struct for items on the checklist.
struct Item
{
public ChecklistEditor ItemEditor { get; set; }
public CheckBox ItemCheckbox { get; set; }
}
// Create a list of contact structs to populate the ListView.
ObservableCollection<Item> items;
// Flag for when an item is added to the list.
bool itemAdded = false;
// Constructor.
public ChecklistPage()
{
// Whatever setup stuff it was going to do anyway.
InitializeComponent();
// Set the label's BindingContext to the
// App class so it can update its text.
tipLabel.BindingContext = (App)App.Current;
}
// Override for OnAppearing().
protected override void OnAppearing()
{
// Makes the page appear.
base.OnAppearing();
// Set the page's title to be the name of the selected list.
Title = App.selectedList.Name;
// Make a toolbar item appear to access the Main Checklist
// unless we are already there.
if (App.selectedList.ListFile.Name.EndsWith(".mchec"))
{
ToolbarItems.Remove(MainChecklistButton);
}
// Set the binding context of the page to itself.
BindingContext = this;
// Start the timer for the tips banner if it is stopped.
App.tipTimer.Start();
// Set the banner's text to the current tip's sumamry.
tipLabel.Text = ((App)App.Current).CurrentTip.Summary;
OnPropertyChanged("CurrentTip");
// Subscribe the OnTipUpdate function to the tipUpdate event in the app
// class.
App.TipUpdate += OnTipUpdate;
// Make the ObservableCOllection reference something.
items = new ObservableCollection<Item>();
// Open a stream to the list that we want to display.
using (StreamReader listReader = new StreamReader(App.selectedList
.ListFile.FullName))
{
// Loop through the file and read data into the list.
while (!listReader.EndOfStream)
{
// Create a blank item.
Item newItem = new Item()
{
ItemEditor = new ChecklistEditor()
{
Text = listReader.ReadLine(),
Placeholder = "New Item",
IsTabStop = true,
AutoSize = EditorAutoSizeOption.TextChanges,
WidthRequest = 310
},
ItemCheckbox = new CheckBox()
{
Color = App.selectedList.ListItemColor,
IsChecked = bool.Parse(listReader.ReadLine())
}
};
// Subscribe OnCompleted() to the new item's "Completed"
// event.
newItem.ItemEditor.Completed += OnCompleted;
// Subscribe OnTextChanged() to the new item's
// "TextChanged" event.
newItem.ItemEditor.TextChanged += OnTextChanged;
// Add the new item to the list.
items.Add(newItem);
// Make the ListView update.
ChecklistView.ItemsSource = items;
OnPropertyChanged("contacts");
}
// Once everything is loaded, close the file.
listReader.Close();
}
}
// Override for OnDisappearing().
protected override void OnDisappearing()
{
// Makes the page disappear.
base.OnDisappearing();
// Open a stream to the file for the list.
StreamWriter listWriter = new StreamWriter(App.selectedList
.ListFile.FullName);
// Loop through the contacts list to write the contacts to the
// file.
for (int i = 0; i < items.Count; i++)
{
// Write each item to the file.
listWriter.WriteLine(items.ElementAt(i).ItemEditor.Text);
listWriter.WriteLine(items.ElementAt(i).ItemCheckbox.IsChecked);
}
// Close the stream.
listWriter.Close();
}
// Function for when the "Add New Contact" button is clicked.
private void OnAddNewItemClicked(object sender, EventArgs e)
{
// Create a blank item.
Item newItem = new Item()
{
ItemEditor = new ChecklistEditor()
{
Placeholder = "New Item",
IsTabStop = true,
AutoSize = EditorAutoSizeOption.TextChanges,
WidthRequest = 310
},
ItemCheckbox = new CheckBox()
{
Color = App.selectedList.ListItemColor,
IsChecked = false
}
};
// Subscribe OnCompleted() to the new item's "Completed"
// event.
newItem.ItemEditor.Completed += OnCompleted;
// Subscribe OnTextChanged() to the new item's
// "TextChanged" event.
newItem.ItemEditor.TextChanged += OnTextChanged;
// Add the new contact to the list.
items.Add(newItem);
// Set the "itemAdded" flag to true.
itemAdded = true;
// Make the ListView update.
ChecklistView.ItemsSource = items;
OnPropertyChanged("contacts");
// Select the new item.
ChecklistView.SelectedItem = items.ElementAt(items.Count - 1);
}
// Function for when an item is selected, used to set the focus to
// a newly added item in the list.
private async void OnItemSelected(object sender, SelectedItemChangedEventArgs e)
{
// Only runs this if an item was added (as opposed to being
// read in from the file).
if (itemAdded)
{
if (e.SelectedItem == null) return;
await Task.Delay(100); // Change the delay time if Focus() doesn't work.
((Item)e.SelectedItem).ItemEditor.Focus();
ChecklistView.SelectedItem = null;
itemAdded = false;
}
}
// Function for when the user presses "Return" on the keyboard in
// an editor.
private void OnCompleted(object sender, EventArgs e)
{
// We just want to unfocus the editor.
((Editor)sender).Unfocus();
}
// Function for when the user types anything in the editor, used
// to make sure it resizes.
private void OnTextChanged(object sender, TextChangedEventArgs e)
{
// Makes the cell resize. The cell is the parent of the
// StackLayout which is the parent of the ContentView which is
// the parent of the Editor that fired the event.
((ViewCell)((Editor)sender).Parent.Parent.Parent)
.ForceUpdateSize();
}
}
}
In Android , you need to set Single Line for EditTextView , then it will works .
For example :
...
protected override void OnElementChanged(ElementChangedEventArgs<Editor> e)
{
base.OnElementChanged(e);
// set single line will works
Control.SetSingleLine();
Control.ImeOptions = ImeAction.Done;
Control.EditorAction += OnEditorAction;
}
private void OnEditorAction(object sender, TextView.EditorActionEventArgs e)
{
e.Handled = false;
if (e.ActionId == ImeAction.Done)
{
Control.ClearFocus();
e.Handled = true;
InputMethodManager imm = (InputMethodManager)Control.Context.GetSystemService(Context.InputMethodService);
imm.HideSoftInputFromWindow(Control.WindowToken, 0);
}
}
...
The effect :
About iOS to achieve that , you can refer to follow code :
[assembly: ExportRenderer(typeof(Editor), typeof(CustomEditorRenderer))]
namespace AppEntryTest.iOS
{
class CustomEditorRenderer : EditorRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Editor> e)
{
base.OnElementChanged(e);
Control.ReturnKeyType = UIReturnKeyType.Done;
}
protected override bool ShouldChangeText(UITextView textView, NSRange range, string text)
{
if (text == "\n")
{
textView.ResignFirstResponder();
return false;
}
return true;
}
}
}
The effect :
====================================Update================================
If need to wrap text in Android , you can set background for EditTextView :
Adding bg_gray_border.xml in Resources/drawable folder :
<?xml version="1.0" encoding="utf-8" ?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#ffffff" />
<stroke
android:width="1dp"
android:color="#DEDEDE" />
<corners android:radius="6dp" />
</shape>
Used in Renderer class :
...
protected override void OnElementChanged(ElementChangedEventArgs<Editor> e)
{
base.OnElementChanged(e);
Control.SetSingleLine();
Control.SetBackgroundResource(Resource.Drawable.bg_gray_border);
Control.ImeOptions = ImeAction.Done;
Control.EditorAction += OnEditorAction;
}
...
The effect :
Add wapped text in iOS ,
...
protected override void OnElementChanged(ElementChangedEventArgs<Editor> e)
{
base.OnElementChanged(e);
Control.ReturnKeyType = UIReturnKeyType.Done;
Control.Layer.BorderColor =UIColor.Gray.CGColor;
Control.Layer.BorderWidth = 1;
Control.Layer.CornerRadius = 5;
}
...
The effect :
Here is the sample project .

How to make a call via dialer programmatically in android Pie and above

I use this code to show the number and when pressing it to make a dial.It is working for android versions before Android Pie.
final Button but= findViewById(R.id.buttond);
but.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
String PhNumber = "6998474783";////example number
final CharSequence[] phones = PhNumber.split(" - ");
AlertDialog.Builder builder = new AlertDialog.Builder(CTX);
builder.setTitle("Επιλογή Τηλεφώνου");
builder.setItems(phones, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int item) {
// Do something with the selection
Intent intent = new Intent(Intent.ACTION_CALL, Uri.parse("tel:" + phones[item].toString()));
if (ActivityCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) {
return; }
startActivity(intent);
}
});
AlertDialog alert = builder.create();
alert.show();
}
});
What I have to change to work with Pie and above?
It shows the phone number but when I press it nothing happens
Solved using ACTION_DIAL without any checkSelfPermission.
Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse("tel:" + phones[item].toString()));
startActivity(intent);
If is there any other way, maybe with telecom-Manager please post it.

Change page in pdfbox with Button

I'm working in JavaFX with PDFbox and I have this problem. I want to scroll Scroll through the pages of a pdf by clicking a button(when I press it I want to go to the next page). When I open the pdf then I show the first page with this code:
BufferedImage bi = new BufferedImage (500,500,BufferedImage.TYPE_INT_RGB);
Image img = SwingFXUtils.toFXImage(bi, null);
BufferedImage firstpage = new BufferedImage (500,500,BufferedImage.TYPE_INT_RGB);
firstpage = pdPages.get(0).convertToImage(BufferedImage.TYPE_INT_RGB, 100);
Image primapagina = SwingFXUtils.toFXImage(firstpage, null);
ImageView v ;
v = new ImageView(primapagina);
Then I created a button like this:
Button avanti = new Button(">>");
avanti.setStyle("-fx-font: 22 arial;");
Finally I created the handler,
avanti.setOnAction(new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent e) {
// final int j = i;
BufferedImage Xpage = null;
try {
Xpage = pdPages.get(8).convertToImage(BufferedImage.TYPE_INT_RGB, 100);
// System.out.printf("\n firstpage %d",t );
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
Image Xpagina = SwingFXUtils.toFXImage(Xpage, null);
v.setImage(Xpagina);
}
});
At the moment I put 8 just to see if it was working, and it was. But How can I ,by clicking the button, go to the next page? I thought about pointers, but I don't think it exist in JavaFX.
Thanks

Android crop intent doesn't work on system gallery app but works on 3rd-party app

I try to pick an image from sdcard and then crop it.
ACTION_PICK is OK, but when i call ACTION_CROP, my system gallery app (I call it as A) can't done the action, but another app (B) can.
I tried the following cases:
1/ Pick by A and then crop by A => pick OK, crop fail
2/ Pick by B and then crop by A => the same as first case.
3/ Pick by A and then crop by B => every things OK.
4/ Pick by B and then crop by B => every things OK.
So my temporary conclusion is: my system app can't do the crop action with my code (may be i forgot something). Here is my code:
ACTION_PICK:
public Intent galleryIntent() {
Intent galleryIntent = new Intent(Intent.ACTION_PICK);
galleryIntent.setType("image/*");
galleryIntent.putExtra("return-data", true);
return galleryIntent;
}
ACTION_CROP:
public Intent cropIntent(Uri inUri, int outputX, int outputY,
boolean isScale) {
Intent cropIntent = new Intent("com.android.camera.action.CROP");
cropIntent.setDataAndType(inUri, "image/*");
cropIntent.putExtra("crop", "true");
cropIntent.putExtra("aspectX", outputX);
cropIntent.putExtra("aspectY", outputY);
cropIntent.putExtra("outputX", outputX);
cropIntent.putExtra("outputY", outputY);
cropIntent.putExtra("scale", isScale);
cropIntent.putExtra("return-data", true);
return cropIntent;
}
My onActivityResult method
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == RESULT_OK) {
switch (requestCode) {
case REQUEST_CODE_GALLERY:
imageUri = data.getData();
startActivityForResult(cropIntent(imageUri,
500, 500, true), REQUEST_CODE_CROP);
break;
case REQUEST_CODE_CROP:
Bundle extras = data.getExtras();
Bitmap tempBitmap = extras.getParcelable("data");
imgvMain.setImageBitmap(null);
imgvMain.setImageBitmap(tempBitmap);
break;
}
} else {
imageUri = null;
}
}
Am i missing somethings?
Thank for your attention!
I use this code successfully for Android 2.2 and up:
It opens a selection of apps that can get image files e.g. the Gallery app. If the selected app can crop, it will also do so.
The cropped image will be saved to the supplied temp file.
(note the small difference for KITKAT).
Intent intent = new Intent();
intent.setType("image/*");
intent.putExtra("crop", "true");
intent.putExtra("outputX", Constants.IMAGE_WIDTH);
intent.putExtra("outputY", Constants.IMAGE_HEIGHT);
intent.putExtra("aspectX", 1);
intent.putExtra("aspectY", 1);
intent.putExtra("scale", true);
intent.putExtra("scaleUpIfNeeded", true);
intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(<a temp file created somewhere>));
intent.putExtra("outputFormat", Bitmap.CompressFormat.PNG.toString());
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT)
{
intent.setAction(Intent.ACTION_GET_CONTENT);
}
else
{
intent.setAction(Intent.ACTION_PICK);
intent.setData(android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
}
startActivityForResult(intent, RESULT_CROP);
EDIT:
I ended up using custom cropping using: https://github.com/biokys/cropimage. It was very easy and I had no more troubles with cropping :-)
I checked the logs by filtering with gallery3d and found out that the stock app is not getting permission to access the uri. Hence it's behaving unexpectedly. That behaviour is different for different platforms.
Solution:
get the uri of the selected image in onActivityResult() for intent ACTION_PICK.
save the image temporarily.
create new URI from the saved image.
pass the new URI to com.android.camera.action.CROP.
Sample code: (Dont copy paste. for simplicity ,I have removed the error checks and async tasks.)
public void pickCroppedPhoto(){
Intent photoPickerIntent = new Intent(Intent.ACTION_PICK);
photoPickerIntent.setType("image/*");
photoPickerIntent.putExtra("crop", "true");
populateCropExtras(activity, photoPickerIntent);
startActivityForResult(photoPickerIntent , REQUEST_CODE_PICK);
}
#Override
protected void onActivityResult(int requestCode, int resultCode, final Intent data) {
if (requestCode == REQUEST_CODE_PICK ) {
File mainImage = saveUriInFile( this,
data.getData(),
getTempFileForMainImage());
if (null == mainImage) {
handleImageSelectionFailure();
}else {
try {
Uri mainFileUri = Uri.fromFile(mainImage);
performCrop(this,mainFileUri);
}catch(Exception e){
handleImageSelectionFailure();
}
}
}else if ( requestCode == PIC_CROP ){
postImageSelection(data.getData());
}
}
Here is my performCrop code which is similar to that of the question.
public static boolean performCrop(Activity activity, Uri picUri) {
try {
Intent cropIntent = new Intent("com.android.camera.action.CROP");
// indicate image type and Uri
cropIntent.setDataAndType(picUri, "image/*");
if (populateCropExtras(activity, cropIntent)) return false;
activity.startActivityForResult(cropIntent, PIC_CROP);
return true;
}
// respond to users whose devices do not support the crop action
catch (ActivityNotFoundException anfe) {
String errorMessage = "Device doesn't support crop!";
Log.w(PhotoPickerUtil.class.getCanonicalName(), errorMessage);
return false;
}catch (IOException ioe){
String errorMessage = "Error while getting temporary file from external storage";
Log.w(PhotoPickerUtil.class.getCanonicalName(), errorMessage);
return false;
}
}
private static void populateCropExtras(Activity activity, Intent cropIntent) throws IOException { {
// set crop properties
cropIntent.putExtra("crop", "true");
// indicate output X and Y
cropIntent.putExtra("outputX", 300);
cropIntent.putExtra("outputY", 300);
// indicate aspect of desired crop
cropIntent.putExtra("aspectX", 1);
cropIntent.putExtra("aspectY", 1);
cropIntent.putExtra("outputFormat", Bitmap.CompressFormat.PNG.toString());
Uri tempUri = Uri.fromFile(getTempFile(activity));
cropIntent.putExtra("output", tempUri);
}

How could I choose one particular file to load with loadStrings

The title is explicit enough, I want to let the user choose the text file he want to open.
I do not know if there is an explorer or an input field already implemented on processing.
Any help would be great.
Use selectInput. From the Processing reference:
Opens a platform-specific file chooser dialog to select a file for input. After the selection is made, the selected File will be passed to the 'callback' function. If the dialog is closed or canceled, null will be sent to the function, so that the program is not waiting for additional input. The callback is necessary because of how threading works.
I've modified the example sketch they provide in the reference to include loading the file with the loadStrings method.
String[] txtFile;
void setup() {
selectInput("Select a file to process:", "fileSelected");
}
void fileSelected(File selection) {
if (selection == null) {
println("Window was closed or the user hit cancel.");
} else {
String filepath = selection.getAbsolutePath();
println("User selected " + filepath);
// load file here
txtFile = loadStrings(filepath);
}
}
There is no Implemented method, but you could could make a buffer and monitor key presses like so:
String[] File;
String keybuffer = "";
Char TriggerKey = Something;
void setup(){
//do whatever here
}
void draw(){
//Optional, to show the current buffer
background(255);
text(keybuffer,100,100);
}
void keyPressed(){
if(keyCode >= 'a' && keyCode <= 'z'){
keybuffer = keybuffer + key;
}
if(key == TriggerKey){
File = loadStrings(keybuffer + ".txt");
}
}
when triggerkey is pressed, it loads the file

Resources