use of com.codename1.util.Wrapper in project CodenameOne by codenameone.
the class Util method downloadUrlSafely.
/**
* <p>
* Safely download the given URL to the Storage or to the FileSystemStorage:
* this method is resistant to network errors and capable of resume the
* download as soon as network conditions allow and in a completely
* transparent way for the user; note that in the global network error
* handling, there must be an automatic
* <pre>.retry()</pre>, as in the code example below.</p>
* <p>
* This method is useful if the server correctly returns Content-Length and
* if it supports partial downloads: if not, it works like a normal
* download.</p>
* <p>
* Pros: always allows you to complete downloads, even if very heavy (e.g.
* 100MB), even if the connection is unstable (network errors) and even if
* the app goes temporarily in the background (on some platforms the
* download will continue in the background, on others it will be
* temporarily suspended).</p>
* <p>
* Cons: since this method is based on splitting the download into small
* parts (512kbytes is the default), this approach causes many GET requests
* that slightly slow down the download and cause more traffic than normally
* needed.</p>
* <p>
* Usage example:</p>
* <script src="https://gist.github.com/jsfan3/554590a12c3102a3d77e17533e7eca98.js"></script>
*
* @param url
* @param fileName must be a valid Storage file name or FileSystemStorage
* file path
* @param percentageCallback invoked (in EDT) during the download to notify
* the progress (from 0 to 100); it can be null if you are not interested in
* monitoring the progress
* @param filesavedCallback invoked (in EDT) only when the download is
* finished; if null, no action is taken
* @throws IOException
*/
public static void downloadUrlSafely(String url, final String fileName, final OnComplete<Integer> percentageCallback, final OnComplete<String> filesavedCallback) throws IOException {
// Code discussion here: https://stackoverflow.com/a/62137379/1277576
String partialDownloadsDir = FileSystemStorage.getInstance().getAppHomePath() + FileSystemStorage.getInstance().getFileSystemSeparator() + "partialDownloads";
if (!FileSystemStorage.getInstance().isDirectory(partialDownloadsDir)) {
FileSystemStorage.getInstance().mkdir(partialDownloadsDir);
}
// do its best to be unique if there are parallel downloads
final String uniqueId = url.hashCode() + "" + downloadUrlSafelyRandom.nextInt();
final String partialDownloadPath = partialDownloadsDir + FileSystemStorage.getInstance().getFileSystemSeparator() + uniqueId;
// as discussed here: https://stackoverflow.com/a/57984257
final boolean isStorage = fileName.indexOf("/") < 0;
// total expected download size, with a check partial download support
final long fileSize = getFileSizeWithoutDownload(url, true);
// 512 kbyte, size of each small download
final int splittingSize = 512 * 1024;
final Wrapper<Long> downloadedTotalBytes = new Wrapper<Long>(0l);
final OutputStream out;
if (isStorage) {
// leave it open to append partial downloads
out = Storage.getInstance().createOutputStream(fileName);
} else {
out = FileSystemStorage.getInstance().openOutputStream(fileName);
}
// Codename One thread that supports crash protection and similar Codename One features.
final EasyThread mergeFilesThread = EasyThread.start("mergeFilesThread");
final ConnectionRequest cr = new GZConnectionRequest();
cr.setUrl(url);
cr.setPost(false);
if (fileSize > splittingSize) {
// Which byte should the download start from?
cr.addRequestHeader("Range", "bytes=0-" + splittingSize);
cr.setDestinationFile(partialDownloadPath);
} else {
Util.cleanup(out);
if (isStorage) {
cr.setDestinationStorage(fileName);
} else {
cr.setDestinationFile(fileName);
}
}
cr.addResponseListener(new ActionListener<NetworkEvent>() {
@Override
public void actionPerformed(NetworkEvent evt) {
mergeFilesThread.run(new Runnable() {
@Override
public void run() {
try {
// We append the just saved partial download to the fileName, if it exists
if (FileSystemStorage.getInstance().exists(partialDownloadPath)) {
InputStream in = FileSystemStorage.getInstance().openInputStream(partialDownloadPath);
Util.copyNoClose(in, out, 8192);
Util.cleanup(in);
// before deleting the file, we check and update how much data we have actually downloaded
downloadedTotalBytes.set(downloadedTotalBytes.get() + FileSystemStorage.getInstance().getLength(partialDownloadPath));
FileSystemStorage.getInstance().delete(partialDownloadPath);
}
// Is the download finished?
if (downloadedTotalBytes.get() > fileSize) {
throw new IllegalStateException("More content has been downloaded than the file length, check the code.");
}
if (fileSize <= 0 || downloadedTotalBytes.get() == fileSize) {
// yes, download finished
Util.cleanup(out);
if (filesavedCallback != null) {
CN.callSerially(new Runnable() {
@Override
public void run() {
filesavedCallback.completed(fileName);
}
});
}
} else {
// no, it's not finished, we repeat the request after updating the "Range" header
cr.addRequestHeader("Range", "bytes=" + downloadedTotalBytes.get() + "-" + (Math.min(downloadedTotalBytes.get() + splittingSize, fileSize)));
NetworkManager.getInstance().addToQueue(cr);
}
} catch (IOException ex) {
Log.p("Error in appending splitted file to output file", Log.ERROR);
Log.e(ex);
Log.sendLogAsync();
}
}
});
}
});
NetworkManager.getInstance().addToQueue(cr);
NetworkManager.getInstance().addProgressListener(new ActionListener<NetworkEvent>() {
@Override
public void actionPerformed(NetworkEvent evt) {
if (cr == evt.getConnectionRequest() && fileSize > 0) {
// the following casting to long is necessary when the file is bigger than 21MB, otherwise the result of the calculation is wrong
if (percentageCallback != null) {
CN.callSerially(new Runnable() {
@Override
public void run() {
percentageCallback.completed((int) ((long) downloadedTotalBytes.get() * 100 / fileSize));
}
});
}
}
}
});
}
use of com.codename1.util.Wrapper in project CodenameOne by codenameone.
the class MigLayout method checkCache.
/**
* Check if something has changed and if so recreate it to the cached
* objects.
*
* @param parent The parent that is the target for this layout manager.
*/
private void checkCache(Container parent) {
if (parent == null) {
return;
}
if (dirty) {
grid = null;
}
cleanConstraintMaps(parent);
// Check if the grid is valid
int mc = PlatformDefaults.getModCount();
if (lastModCount != mc) {
grid = null;
lastModCount = mc;
}
// if (!parent.isValid()) {
if (!lastWasInvalid) {
lastWasInvalid = true;
int hash = 0;
// Added in 3.7.3 to resolve a timing regression introduced in 3.7.1
boolean resetLastInvalidOnParent = false;
for (ComponentWrapper wrapper : ccMap.keySet()) {
Object component = wrapper.getComponent();
if (component instanceof TextArea) {
resetLastInvalidOnParent = true;
}
hash ^= wrapper.getLayoutHashCode();
hash += 285134905;
}
if (resetLastInvalidOnParent) {
resetLastInvalidOnParent(parent);
}
if (hash != lastHash) {
grid = null;
lastHash = hash;
}
Dimension ps = new Dimension(parent.getWidth(), parent.getHeight());
if (lastInvalidSize == null || !lastInvalidSize.equals(ps)) {
grid = null;
lastInvalidSize = ps;
}
}
/*} else {
lastWasInvalid = false;
}*/
ContainerWrapper par = checkParent(parent);
setDebug(par, getDebugMillis() > 0);
if (grid == null) {
grid = new Grid(par, lc, rowSpecs, colSpecs, ccMap, callbackList);
}
dirty = false;
}
use of com.codename1.util.Wrapper in project CodenameOne by codenameone.
the class AndroidImplementation method addActionsToNotification.
/**
* Adds actions to a push notification. This is called by the Push broadcast receiver probably before
* Codename One is initialized
* @param provider Reference to the app's main class which implements PushActionsProvider
* @param categoryId The category ID of the push notification.
* @param builder The builder for the push notification.
* @param targetIntent The target intent... this should go to the app's main Activity.
* @param context The current context (inside the Broadcast receiver).
* @throws IOException
*/
public static void addActionsToNotification(PushActionsProvider provider, String categoryId, NotificationCompat.Builder builder, Intent targetIntent, Context context) throws IOException {
// NOTE: THis will likely run when the main activity isn't running so we won't have
// access to any display properties... just native Android APIs will be accessible.
PushActionCategory category = null;
PushActionCategory[] categories;
if (provider != null) {
categories = provider.getPushActionCategories();
} else {
categories = getInstalledPushActionCategories(context);
}
for (PushActionCategory candidateCategory : categories) {
if (categoryId.equals(candidateCategory.getId())) {
category = candidateCategory;
break;
}
}
if (category == null) {
return;
}
int requestCode = 1;
for (PushAction action : category.getActions()) {
Intent newIntent = (Intent) targetIntent.clone();
newIntent.putExtra("pushActionId", action.getId());
PendingIntent contentIntent = createPendingIntent(context, requestCode++, newIntent);
try {
int iconId = 0;
try {
iconId = Integer.parseInt(action.getIcon());
} catch (Exception ex) {
}
// android.app.Notification.Action.Builder actionBuilder = new android.app.Notification.Action.Builder(iconId, action.getTitle(), contentIntent);
System.out.println("Adding action " + action.getId() + ", " + action.getTitle() + ", icon=" + iconId);
if (ActionWrapper.BuilderWrapper.isSupported()) {
// We need to take this abstracted "wrapper" approach because the Action.Builder class, and RemoteInput class
// aren't available until API 22.
// These classes use reflection to provide support for these classes safely.
ActionWrapper.BuilderWrapper actionBuilder = new ActionWrapper.BuilderWrapper(iconId, action.getTitle(), contentIntent);
if (action.getTextInputPlaceholder() != null && RemoteInputWrapper.isSupported()) {
RemoteInputWrapper.BuilderWrapper remoteInputBuilder = new RemoteInputWrapper.BuilderWrapper(action.getId() + "$Result");
remoteInputBuilder.setLabel(action.getTextInputPlaceholder());
RemoteInputWrapper remoteInput = remoteInputBuilder.build();
actionBuilder.addRemoteInput(remoteInput);
}
ActionWrapper actionWrapper = actionBuilder.build();
new NotificationCompatWrapper.BuilderWrapper(builder).addAction(actionWrapper);
} else {
builder.addAction(iconId, action.getTitle(), contentIntent);
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
use of com.codename1.util.Wrapper in project CodenameOne by codenameone.
the class AutoCompleteTextField method addPopup.
private void addPopup() {
final Form f = getComponentForm();
popup.removeAll();
popup.setVisible(false);
popup.setEnabled(false);
filter(getText());
final com.codename1.ui.List l = new com.codename1.ui.List(getSuggestionModel());
if (getMinimumElementsShownInPopup() > 0) {
l.setMinElementHeight(getMinimumElementsShownInPopup());
}
l.setScrollToSelected(false);
l.setItemGap(0);
for (ActionListener al : listeners) {
l.addActionListener(al);
}
if (completionRenderer == null) {
((DefaultListCellRenderer<String>) l.getRenderer()).setShowNumbers(false);
} else {
l.setRenderer(completionRenderer);
}
l.setUIID("AutoCompleteList");
l.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
pickedText = (String) l.getSelectedItem();
setParentText(pickedText);
fireActionEvent();
// relaunch text editing if we are still editing
if (Display.getInstance().isTextEditing(AutoCompleteTextField.this)) {
Display.getInstance().editString(AutoCompleteTextField.this, getMaxSize(), getConstraint(), (String) l.getSelectedItem());
}
popup.setVisible(false);
popup.setEnabled(false);
f.repaint();
}
});
byte[] units = popup.getStyle().getMarginUnit();
if (units != null) {
units[Component.LEFT] = Style.UNIT_TYPE_PIXELS;
units[Component.TOP] = Style.UNIT_TYPE_PIXELS;
popup.getAllStyles().setMarginUnit(units);
}
popup.getAllStyles().setMargin(LEFT, Math.max(0, getAbsoluteX()));
int popupHeight = calcPopuupHeight(l);
popup.setPreferredW(getWidth());
popup.setHeight(popupHeight);
popup.setWidth(getWidth());
popup.addComponent(l);
popup.layoutContainer();
// block the reflow of this popup, which can cause painting problems
dontCalcSize = true;
if (f != null) {
if (popup.getParent() == null) {
Container lay = f.getLayeredPane(AutoCompleteTextField.this.getClass(), true);
lay.setLayout(new LayeredLayout());
Container wrapper = new Container();
wrapper.add(popup);
lay.addComponent(wrapper);
}
f.revalidate();
}
}
use of com.codename1.util.Wrapper in project CodenameOne by codenameone.
the class AutoCompleteTextField method addPopup.
private void addPopup(boolean updateFilter) {
final Form f = getComponentForm();
popup.removeAll();
popup.setVisible(false);
popup.setEnabled(false);
if (updateFilter) {
filter(getText());
}
final com.codename1.ui.List l = new com.codename1.ui.List(getSuggestionModel());
if (getMinimumElementsShownInPopup() > 0) {
l.setMinElementHeight(getMinimumElementsShownInPopup());
}
l.setScrollToSelected(false);
l.setItemGap(0);
for (ActionListener al : listeners) {
l.addActionListener(al);
}
if (completionRenderer == null) {
((DefaultListCellRenderer<String>) l.getRenderer()).setShowNumbers(false);
} else {
l.setRenderer(completionRenderer);
}
l.setUIID("AutoCompleteList");
l.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
if (shouldShowPopup()) {
pickedText = (String) l.getSelectedItem();
setParentText(pickedText);
fireActionEvent();
// relaunch text editing if we are still editing
if (Display.getInstance().isTextEditing(AutoCompleteTextField.this)) {
Display.getInstance().editString(AutoCompleteTextField.this, getMaxSize(), getConstraint(), (String) l.getSelectedItem());
}
popup.setVisible(false);
popup.setEnabled(false);
f.revalidate();
}
}
});
byte[] units = popup.getStyle().getMarginUnit();
if (units != null) {
units[Component.LEFT] = Style.UNIT_TYPE_PIXELS;
units[Component.TOP] = Style.UNIT_TYPE_PIXELS;
popup.getAllStyles().setMarginUnit(units);
}
int leftMargin = isRTL() ? Math.max(0, f.getWidth() - getAbsoluteX() - getWidth()) : Math.max(0, getAbsoluteX());
popup.getAllStyles().setMargin(LEFT, leftMargin);
int popupHeight = calcPopupHeight(l);
popup.setPreferredW(getWidth());
popup.setHeight(popupHeight);
popup.setWidth(getWidth());
popup.addComponent(l);
popup.layoutContainer();
// block the reflow of this popup, which can cause painting problems
dontCalcSize = true;
if (f != null) {
if (popup.getParent() == null) {
Container lay = f.getLayeredPane(AutoCompleteTextField.this.getClass(), true);
lay.setLayout(new LayeredLayout());
Container wrapper = new Container();
wrapper.add(popup);
lay.addComponent(wrapper);
}
f.revalidate();
}
}
Aggregations