Browse Source

Manage upload multiple files at the same time

Fixe #3
tags/release-v2.2
Schoumi 3 years ago
parent
commit
ffb9abf5fc
20 changed files with 1483 additions and 473 deletions
  1. +3
    -3
      app/build.gradle
  2. +28
    -21
      app/src/main/AndroidManifest.xml
  3. +170
    -0
      app/src/main/java/fr/mobdev/goblim/ImageListAdapter.java
  4. +95
    -0
      app/src/main/java/fr/mobdev/goblim/ImageLoader.java
  5. +117
    -0
      app/src/main/java/fr/mobdev/goblim/MultiLinkAdapter.java
  6. +350
    -251
      app/src/main/java/fr/mobdev/goblim/NetworkManager.java
  7. +3
    -3
      app/src/main/java/fr/mobdev/goblim/activity/LinkActivity.java
  8. +220
    -0
      app/src/main/java/fr/mobdev/goblim/activity/MultiLinkActivity.java
  9. +286
    -115
      app/src/main/java/fr/mobdev/goblim/activity/UploadActivity.java
  10. +11
    -4
      app/src/main/java/fr/mobdev/goblim/listener/NetworkAdapter.java
  11. +7
    -4
      app/src/main/java/fr/mobdev/goblim/listener/NetworkListener.java
  12. +17
    -35
      app/src/main/res/layout-land/upload.xml
  13. +32
    -0
      app/src/main/res/layout/link_item.xml
  14. +74
    -0
      app/src/main/res/layout/multilink.xml
  15. +31
    -0
      app/src/main/res/layout/thumb_items.xml
  16. +16
    -36
      app/src/main/res/layout/upload.xml
  17. +10
    -0
      app/src/main/res/menu/menu_upload.xml
  18. +5
    -0
      app/src/main/res/values-fr/strings.xml
  19. +7
    -0
      app/src/main/res/values/strings.xml
  20. +1
    -1
      build.gradle

+ 3
- 3
app/build.gradle View File

@@ -2,7 +2,7 @@ apply plugin: 'com.android.application'

android {
compileSdkVersion 25
buildToolsVersion "24.0.3"
buildToolsVersion "25.0.2"

defaultConfig {
applicationId "fr.mobdev.goblim"
@@ -22,6 +22,6 @@ android {

dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:25.0.0'
compile 'com.android.support:design:25.0.0'
compile 'com.android.support:appcompat-v7:25.1.0'
compile 'com.android.support:design:25.1.0'
}

+ 28
- 21
app/src/main/AndroidManifest.xml View File

@@ -1,8 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="fr.mobdev.goblim" >

<uses-sdk android:minSdkVersion="11"/>
package="fr.mobdev.goblim">

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
@@ -15,37 +13,46 @@
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity
android:name="fr.mobdev.goblim.activity.UploadActivity"
android:name=".activity.UploadActivity"
android:label="@string/app_name"
android:theme="@style/AppTheme.NoActionBar"
>
android:theme="@style/AppTheme.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.SEND" />

<category android:name="android.intent.category.DEFAULT" />

<data android:mimeType="image/*" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="image/*"/>
<action android:name="android.intent.action.SEND_MULTIPLE" />

<category android:name="android.intent.category.DEFAULT" />

<data android:mimeType="image/*" />
</intent-filter>
</activity>
<activity
android:name="fr.mobdev.goblim.activity.HistoryActivity"
android:name=".activity.HistoryActivity"
android:label="@string/app_name"
android:theme="@style/AppTheme.NoActionBar"
>
android:theme="@style/AppTheme.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name="fr.mobdev.goblim.activity.LinkActivity"
android:name=".activity.LinkActivity"
android:label="@string/title_activity_link"
android:theme="@style/AppTheme.NoActionBar"
>
</activity>
android:theme="@style/AppTheme.NoActionBar"/>
<activity
android:name="fr.mobdev.goblim.activity.ServersActivity"
android:name=".activity.ServersActivity"
android:label="@string/title_activity_servers"
android:theme="@style/AppTheme.NoActionBar"
>
</activity>
android:theme="@style/AppTheme.NoActionBar"/>
<activity
android:name=".activity.MultiLinkActivity"
android:label="@string/title_share_links"
android:theme="@style/AppTheme.NoActionBar"/>
</application>
</manifest>

</manifest>

+ 170
- 0
app/src/main/java/fr/mobdev/goblim/ImageListAdapter.java View File

@@ -0,0 +1,170 @@
/*
* Copyright (C) 2017 Anthony Chomienne, anthony@mob-dev.fr
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 3
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

package fr.mobdev.goblim;

import android.content.Context;
import android.graphics.Bitmap;
import android.net.Uri;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.ProgressBar;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;

public class ImageListAdapter extends RecyclerView.Adapter<ImageViewHolder> {

private OnImageClickListener listener;
private LinkedHashMap<Uri,Bitmap> datas;
private ImageLoader loader;
private ImageLoader.ImageLoaderListener loaderListener;

public ImageListAdapter(OnImageClickListener listener, ImageLoader.ImageLoaderListener loaderListener){
datas = new LinkedHashMap<>();
this.listener = listener;
this.loaderListener = loaderListener;
}

public void setBitmapsForUris(Context context, List<Uri> imageUris, List<Bitmap> bitmaps) {
int i = 0;
if(bitmaps.size() != imageUris.size())
return;
for(Uri imUri : imageUris) {
setBitmapForUri(imUri,bitmaps.get(i));
if(bitmaps.get(i) == null) {
ImageLoader.Message mes = new ImageLoader.Message();
mes.context = context;
mes.uri = imUri;
addMessageToQueue(mes);
}
i++;

}
}

public void setBitmapForUri(Uri imageUri, Bitmap thumb) {
if(datas.containsKey(imageUri)) {
datas.put(imageUri, thumb);
notifyDataSetChanged();
}
}

public void addUris(Context context, List<Uri> uris, boolean loadImg) {
for(Uri imUri : uris) {
if(loadImg) {
addUri(context,imUri);
} else {
datas.put(imUri,null);
}
}
notifyDataSetChanged();
}

public void addUri(Context context, Uri imUri) {
datas.put(imUri,null);
ImageLoader.Message mes = new ImageLoader.Message();
mes.context = context;
mes.uri = imUri;
addMessageToQueue(mes);
notifyDataSetChanged();
}

private void addMessageToQueue(ImageLoader.Message mes) {
if(loader == null || loader.getState() == Thread.State.TERMINATED) {
loader = new ImageLoader(loaderListener);
loader.start();
}
loader.addMessageToQueue(mes);
}

public Bitmap getForUri(Uri imUri) {
return datas.get(imUri);
}

@Override
public ImageViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.thumb_items,parent,false);
return new ImageViewHolder(v);
}

@Override
public void onBindViewHolder(ImageViewHolder holder, int position) {
List<Uri> uris = new ArrayList<>(datas.keySet());
final Uri imUri = uris.get(position);
Bitmap bt = datas.get(imUri);
if(bt != null) {
holder.thumb.setVisibility(View.VISIBLE);
holder.thumb.setImageBitmap(bt);
holder.progressLoad.setVisibility(View.GONE);
} else {
holder.thumb.setVisibility(View.GONE);
holder.progressLoad.setVisibility(View.VISIBLE);
}
holder.resetBt.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
datas.remove(imUri);
listener.onRemove(imUri);
notifyDataSetChanged();
}
});
holder.thumb.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
listener.OnImageClick(imUri);
}
});
}

@Override
public int getItemCount() {
return datas.size();
}

public ArrayList<Bitmap> getBitmaps() {
return new ArrayList<>(datas.values());
}

public void clear() {
datas.clear();
notifyDataSetChanged();
}

public interface OnImageClickListener{
void OnImageClick(Uri imageUri);
void onRemove(Uri imageUri);
}
}

class ImageViewHolder extends RecyclerView.ViewHolder {
ProgressBar progressLoad;
ImageView resetBt;
ImageView thumb;
ImageViewHolder(View v) {
super(v);
resetBt = (ImageView) v.findViewById(R.id.reset_img);
thumb = (ImageView) v.findViewById(R.id.thumbnail_item);
progressLoad = (ProgressBar) v.findViewById(R.id.progress_thumb);
}
}


+ 95
- 0
app/src/main/java/fr/mobdev/goblim/ImageLoader.java View File

@@ -0,0 +1,95 @@
/*
* Copyright (C) 2017 Anthony Chomienne, anthony@mob-dev.fr
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 3
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

package fr.mobdev.goblim;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.util.DisplayMetrics;

import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Semaphore;

public class ImageLoader extends Thread {

private Semaphore sem;
private List<Message> messages;
private ImageLoaderListener listener;

ImageLoader(ImageLoaderListener listener) {
sem = new Semaphore(0,true);
messages = new ArrayList<>();
this.listener = listener;
}

void addMessageToQueue(Message mes) {
messages.add(mes);
sem.release();
}

@Override
public void run(){
boolean isRunning = true;
while (isRunning){
try {
sem.acquire();
if (messages.size() > 0) {
Message mes = messages.get(0);
Bitmap thumb = loadThumb(mes);
if(thumb != null) {
listener.thumbLoaded(mes.uri, thumb);
}
}
}catch(InterruptedException e){
e.printStackTrace();
isRunning = false;
}
messages.remove(0);
if(messages.isEmpty())
listener.allThumbLoaded();
}
}

private Bitmap loadThumb(Message mes) {
Bitmap thumb = null;
try {
BitmapFactory.Options opts = new BitmapFactory.Options();
opts.inScaled = true;
opts.inDensity = 640;
opts.inTargetDensity = DisplayMetrics.DENSITY_MEDIUM;
thumb = BitmapFactory.decodeStream(mes.context.getContentResolver().openInputStream(mes.uri), null, opts);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
return thumb;
}

static class Message {
Context context;
Uri uri;
}

public interface ImageLoaderListener{
void thumbLoaded(Uri imageUri, Bitmap thumb);
void allThumbLoaded();
}
}

+ 117
- 0
app/src/main/java/fr/mobdev/goblim/MultiLinkAdapter.java View File

@@ -0,0 +1,117 @@
/*
* Copyright (C) 2017 Anthony Chomienne, anthony@mob-dev.fr
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 3
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

package fr.mobdev.goblim;

import android.graphics.Bitmap;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CheckBox;
import android.widget.ImageView;
import android.widget.ProgressBar;

import java.util.ArrayList;
import java.util.List;

public class MultiLinkAdapter extends RecyclerView.Adapter<MultiLinkViewHolder>{

private List<Bitmap> bitmaps;
private List<Integer> selecteds;

public MultiLinkAdapter(int length) {
bitmaps = new ArrayList<>();
selecteds = new ArrayList<>();
for(int i = 0; i < length; i++) {
bitmaps.add(null);
selecteds.add(i);
}
}

public void setBitmap(Bitmap bt, int position) {
bitmaps.set(position,bt);
notifyDataSetChanged();
}

public List<Integer> getSelecteds() {
return selecteds;
}

@Override
public MultiLinkViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.link_item,parent,false);
final MultiLinkViewHolder holder = new MultiLinkViewHolder(v);
View.OnClickListener listener = new View.OnClickListener() {
@Override
public void onClick(View v) {
CheckBox box;
if(v instanceof CheckBox) {
box = (CheckBox) v;
} else {
box = (CheckBox) v.findViewById(R.id.selected);
}
if(box.isChecked() && !selecteds.contains(holder.index)) {
selecteds.add(holder.index);
} else {
selecteds.remove(holder.index);
}
}
};

CheckBox box = (CheckBox) v.findViewById(R.id.selected);
box.setOnClickListener(listener);
v.setOnClickListener(listener);

return holder;
}

@Override
public void onBindViewHolder(MultiLinkViewHolder holder, int position) {
Bitmap bt = bitmaps.get(position);
if(bt != null) {
holder.progress.setVisibility(View.GONE);
holder.thumb.setImageBitmap(bt);
holder.thumb.setVisibility(View.VISIBLE);
} else {
holder.progress.setVisibility(View.VISIBLE);
holder.thumb.setVisibility(View.GONE);
}
holder.box.setChecked(selecteds.contains(position));
}

@Override
public int getItemCount() {
return bitmaps.size();
}
}

class MultiLinkViewHolder extends RecyclerView.ViewHolder {

int index;
CheckBox box;
ImageView thumb;
ProgressBar progress;

MultiLinkViewHolder(View itemView) {
super(itemView);
box = (CheckBox) itemView.findViewById(R.id.selected);
thumb = (ImageView) itemView.findViewById(R.id.thumbnail_link);
progress = (ProgressBar) itemView.findViewById(R.id.progress);
}
}

+ 350
- 251
app/src/main/java/fr/mobdev/goblim/NetworkManager.java View File

@@ -26,7 +26,10 @@ import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.concurrent.Semaphore;

import org.json.JSONException;
import org.json.JSONObject;
@@ -43,288 +46,384 @@ import fr.mobdev.goblim.listener.NetworkListener;
import fr.mobdev.goblim.objects.Img;

public class NetworkManager {
private NetworkListener listener;
private static NetworkManager instance;

private NetworkManager(NetworkListener listener)
{
this.listener = listener;
}
public static NetworkManager getInstance(NetworkListener listener) {
if (instance == null)
instance = new NetworkManager(listener);
if(listener != null && listener != instance.getListener())
instance.setListener(listener);
return instance;
}
private NetworkListener getListener()
{
return listener;
}
private void setListener(NetworkListener listener)
{
this.listener = listener;
}
public void upload(final Context context, final String siteUrl, final int nbDays, final Uri imageUri)
{
if(!isConnectedToInternet(context)) {
listener.fileUploadError(context.getString(R.string.no_network));
return;
}
new Thread(new Runnable() {

@Override
public void run() {
Img img = uploadImage(context, siteUrl, nbDays,imageUri);
if(img != null)
listener.fileUploaded(img);
}
}).start();
}

private Img uploadImage(Context context, String siteUrl, int nbDays, Uri imageUri) {

URL url = null;
Img imgOutput = null;
try {
url = new URL(siteUrl);
} catch (MalformedURLException e1) {
e1.printStackTrace();
}

HttpURLConnection conn = null;
InputStream stream = null;
DataOutputStream request = null;
try {
if(isConnectedToInternet(context))
{
String crlf = "\r\n";
String hyphens = "--";
String boundary = "------------------------dd8a045fcc22b35c";

private NetworkListener listener;
private static NetworkManager instance;
private NetworkThread thread;

private NetworkManager(NetworkListener listener) {
this.listener = listener;
}

public static NetworkManager getInstance(NetworkListener listener) {
if (instance == null)
instance = new NetworkManager(listener);
if (listener != null && listener != instance.getListener())
instance.setListener(listener);
return instance;
}

private NetworkListener getListener() {
return listener;
}

private void setListener(NetworkListener listener) {
this.listener = listener;
if(thread != null)
thread.setListener(listener);
}

public void delete(Context context, String deleteUrl) {
if (!isConnectedToInternet(context)) {
listener.fileUploadError(null, context.getString(R.string.no_network));
return;
}
NetworkThread.Message mes = new NetworkThread.Message();
mes.type = NetworkThread.Message_Type.DELETE_IMG;
mes.url = deleteUrl;
mes.context = context;
addMessageToQueue(mes);
}

public void upload(Context context, String siteUrl, int nbDays, List<Uri> imageUris) {
int i = 1;
for(Uri uri : imageUris) {
NetworkThread.Message mes = new NetworkThread.Message();
mes.type = NetworkThread.Message_Type.UPLOAD_IMG;
mes.context = context;
mes.url = siteUrl;
mes.nbDays = nbDays;
mes.imageUri = uri;
mes.fileNb = imageUris.size();
mes.fileNo = i++;
addMessageToQueue(mes);
}
}

private void addMessageToQueue(NetworkThread.Message mes){

if(thread == null || thread.getState() == Thread.State.TERMINATED) {
thread = new NetworkThread();
thread.setListener(listener);
thread.start();
}
thread.addMessageQueue(mes);
}




private boolean isConnectedToInternet(Context context) {
//verify the connectivity
ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();
if (networkInfo != null) {
State networkState = networkInfo.getState();
if (networkState.equals(State.CONNECTED)) {
return true;
}
}
return false;
}

public void deleteMultiple(Context context, List<String> urls) {
for(String url : urls) {
delete(context,url);
}
}
}

class NetworkThread extends Thread{

private Semaphore sem;
private List<Message> messages;
private NetworkListener listener;

NetworkThread() {
sem = new Semaphore(0,true);
messages = new ArrayList<>();
}

void setListener(NetworkListener lst){
listener = lst;
}

void addMessageQueue(Message mes) {
messages.add(mes);
sem.release();
}

@Override
public void run(){
boolean isRunning = true;
while(isRunning) {
try {
sem.acquire();
Message mes = messages.get(0);
switch (mes.type){
case DELETE_IMG:
deleteImage(mes.context,mes.url);
break;
case UPLOAD_IMG:
uploadImage(mes.context,mes.url,mes.nbDays,mes.imageUri,mes.fileNo,mes.fileNb);
break;
}

} catch (InterruptedException e) {
e.printStackTrace();
isRunning = false;
}
messages.remove(0);
}

}

private void deleteImage(final Context context, final String deleteUrl) {
if (!isConnectedToInternet(context))
listener.deleteError(context.getString(R.string.no_network));
URL url = null;
try {
url = new URL(deleteUrl);
} catch (Exception e) {
e.printStackTrace();
}

if (url != null) {
try {
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
if (urlConnection != null) {
InputStream stream = urlConnection.getInputStream();
stream.close();
} else {
listener.deleteError(context.getString(R.string.network_error));
}
} catch (Exception e) {
e.printStackTrace();
}
listener.deleteSucceed(deleteUrl);
}
}

private void uploadImage(Context context, String siteUrl, int nbDays, Uri imageUri, int fileNo, int fileNb) {
if (!isConnectedToInternet(context)) {
listener.fileUploadError(imageUri,context.getString(R.string.no_network));
return;
}

URL url = null;
Img imgOutput = null;
try {
url = new URL(siteUrl);
} catch (MalformedURLException e1) {
e1.printStackTrace();
}

HttpURLConnection conn = null;
InputStream stream = null;
DataOutputStream request = null;
try {
if (isConnectedToInternet(context)) {
String crlf = "\r\n";
String hyphens = "--";
String boundary = "------------------------dd8a045fcc22b35c";
//check if there is a HTTP 301 Error
if(url != null) {
if (url != null) {
conn = (HttpURLConnection) url.openConnection();
} else {
listener.fileUploadError(imageUri, context.getString(R.string.connection_failed));
return;
}
else {
listener.fileUploadError(context.getString(R.string.connection_failed));
return null;
}
String location = conn.getHeaderField("Location");
if(location != null) {
String location = conn.getHeaderField("Location");
if (location != null) {
//if there is follow the new destination
siteUrl = location;
url = new URL(location);
}
conn = (HttpURLConnection) url.openConnection();
siteUrl = location;
url = new URL(location);
}
conn = (HttpURLConnection) url.openConnection();
//prepare the connection for upload
conn.setRequestMethod("POST");
conn.setUseCaches(false);
conn.setDoInput(true);
conn.setDoOutput(true);
conn.setRequestMethod("POST");
conn.setUseCaches(false);
conn.setDoInput(true);
conn.setDoOutput(true);

conn.setRequestProperty("User-Agent", "Goblim");
conn.setRequestProperty("User-Agent", "Goblim");

conn.setRequestProperty("Expect", "100-continue");
conn.setRequestProperty("Accept", "*/*");
conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);
conn.setRequestProperty("Expect", "100-continue");
conn.setRequestProperty("Accept", "*/*");
conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);

int request_size = 0;
int request_size = 0;

//ask for JSON answer
String answer = hyphens + boundary + crlf;
answer += "Content-Disposition: form-data; name=\"format\"" + crlf;
answer += crlf;
answer += "json" + crlf;
request_size += answer.length();
String answer = hyphens + boundary + crlf;
answer += "Content-Disposition: form-data; name=\"format\"" + crlf;
answer += crlf;
answer += "json" + crlf;
request_size += answer.length();

//ask for storage duration
String duration = hyphens + boundary + crlf;
duration += "Content-Disposition: form-data; name=\"delete-day\"" + crlf;
duration += crlf;
duration += nbDays + crlf;
request_size += duration.length();
String[] proj = {OpenableColumns.DISPLAY_NAME,OpenableColumns.SIZE};
Cursor cursor = context.getContentResolver().query(imageUri,proj,null,null,null);
String fileName = null;
long size = 0;
if(cursor != null && cursor.moveToFirst()) {
fileName = cursor.getString(0);
size = cursor.getLong(1);
cursor.close();
}
String duration = hyphens + boundary + crlf;
duration += "Content-Disposition: form-data; name=\"delete-day\"" + crlf;
duration += crlf;
duration += nbDays + crlf;
request_size += duration.length();
String[] proj = {OpenableColumns.DISPLAY_NAME, OpenableColumns.SIZE};
Cursor cursor = context.getContentResolver().query(imageUri, proj, null, null, null);
String fileName = null;
long size = 0;
if (cursor != null && cursor.moveToFirst()) {
fileName = cursor.getString(0);
size = cursor.getLong(1);
cursor.close();
}

//setup filename and say that octets follow
String outputInformations = hyphens + boundary + crlf;
outputInformations += "Content-Disposition: form-data; name=\"file\"; filename=\"" + fileName + "\"" + crlf;
outputInformations += "Content-Type: application/octet-stream" + crlf;
outputInformations += crlf;
request_size += outputInformations.length();
String outputInformations = hyphens + boundary + crlf;
outputInformations += "Content-Disposition: form-data; name=\"file\"; filename=\"" + fileName + "\"" + crlf;
outputInformations += "Content-Type: application/octet-stream" + crlf;
outputInformations += crlf;
request_size += outputInformations.length();

request_size += size;
request_size += size;

//finish the format http post packet
String endHttp = crlf;
endHttp += hyphens + boundary + hyphens + crlf;
request_size += endHttp.length();

conn.setFixedLengthStreamingMode(request_size);

//write data
request = new DataOutputStream(conn.getOutputStream());
request.writeBytes(answer);
request.writeBytes(duration);
request.writeBytes(outputInformations);
request.flush();
InputStream streamIn = null;
try{
streamIn = context.getContentResolver().openInputStream(imageUri);
}
catch (Exception e)
{
e.printStackTrace();
}
//read data from the file and write it
if(streamIn != null) {
int readed = 0;
int byteWriten = 0;
int blockSize = 1024;
byte[] buffer = new byte[blockSize];
while(readed != -1) {
try {
readed = streamIn.read(buffer);
if(readed != -1) {
request.write(buffer, 0, readed);
byteWriten+=readed;
listener.uploadProgress(byteWriten,(int)size);
}
} catch (IOException e) {
e.printStackTrace();
readed = -1;
}
}
}
request.writeBytes(endHttp);
request.flush();
String endHttp = crlf;
endHttp += hyphens + boundary + hyphens + crlf;
request_size += endHttp.length();

conn.setFixedLengthStreamingMode(request_size);

//write data
request = new DataOutputStream(conn.getOutputStream());
request.writeBytes(answer);
request.writeBytes(duration);
request.writeBytes(outputInformations);
request.flush();
InputStream streamIn = null;
try {
streamIn = context.getContentResolver().openInputStream(imageUri);
} catch (Exception e) {
e.printStackTrace();
}
//read data from the file and write it
if (streamIn != null) {
int readed = 0;
int byteWriten = 0;
int blockSize = 1024;
byte[] buffer = new byte[blockSize];
while (readed != -1) {
try {
readed = streamIn.read(buffer);
if (readed != -1) {
request.write(buffer, 0, readed);
byteWriten += readed;
listener.uploadProgress(byteWriten, (int) size, fileNo, fileNb);
}
} catch (IOException e) {
e.printStackTrace();
readed = -1;
}
}
}
request.writeBytes(endHttp);
request.flush();

//get answer
stream = conn.getInputStream();
}
} catch (IOException e1) {
if(conn != null) {
stream = conn.getErrorStream();
stream = conn.getInputStream();
}
else {
} catch (IOException e1) {
if (conn != null) {
stream = conn.getErrorStream();
} else {
e1.printStackTrace();
listener.fileUploadError(context.getString(R.string.network_error));
listener.fileUploadError(imageUri, context.getString(R.string.network_error));
return;
}
}
}

if(stream != null) {
if (stream != null) {
//prepare JSON reading
InputStreamReader isr = new InputStreamReader(stream);
BufferedReader br = new BufferedReader(isr);
boolean isReading = true;
String data;
String jsonStr = "";
InputStreamReader isr = new InputStreamReader(stream);
BufferedReader br = new BufferedReader(isr);
boolean isReading = true;
String data;
String jsonStr = "";
//get all data in a String
do {
try {
data = br.readLine();
if (data != null)
jsonStr += data;
else
isReading = false;
} catch (IOException e) {
e.printStackTrace();
isReading = false;
}
} while (isReading);
//parse JSON answer
try {
if(request != null)
do {
try {
data = br.readLine();
if (data != null)
jsonStr += data;
else
isReading = false;
} catch (IOException e) {
e.printStackTrace();
isReading = false;
}
} while (isReading);
//parse JSON answer
try {
if (request != null)
request.close();
stream.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
// Parse the JSON to a JSONObject
JSONObject rootObject = new JSONObject(jsonStr);
// Get msg (root) element
String msgStr = rootObject.getString("msg");
stream.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
// Parse the JSON to a JSONObject
JSONObject rootObject = new JSONObject(jsonStr);
// Get msg (root) element
String msgStr = rootObject.getString("msg");
// is there an error?
if(!msgStr.contains("{")) {
listener.fileUploadError(msgStr);
return null;
}
else if(rootObject.has("msg")){
if (!msgStr.contains("{")) {
listener.fileUploadError(imageUri, msgStr);
return;
} else if (rootObject.has("msg")) {
//retrieve useful data
JSONObject msg = rootObject.getJSONObject("msg");
String hashOutput = msg.getString("short");
String realHashOutput = msg.getString("real_short");
String token = msg.getString("token");
imgOutput = new Img(0, siteUrl, hashOutput, realHashOutput, Calendar.getInstance(), nbDays, null,token);
}
} catch (JSONException e) {
e.printStackTrace();
listener.fileUploadError(context.getString(R.string.unreadable_json));
}
}
return imgOutput;
}

public void deleteImage(final Context context, final String deleteUrl) {
new Thread(new Runnable() {
@Override
public void run() {

if (!isConnectedToInternet(context))
listener.deleteError(context.getString(R.string.no_network));
URL url = null;
try {
url = new URL(deleteUrl);
} catch (Exception e) {
e.printStackTrace();
}

if (url != null) {
try {
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
if(urlConnection != null) {
InputStream stream = urlConnection.getInputStream();
stream.close();
}
else {
listener.deleteError(context.getString(R.string.network_error));
}
} catch (Exception e) {
e.printStackTrace();
}
listener.deleteSucceed();
String hashOutput = msg.getString("short");
String realHashOutput = msg.getString("real_short");
String token = msg.getString("token");
imgOutput = new Img(0, siteUrl, hashOutput, realHashOutput, Calendar.getInstance(), nbDays, null, token);
}
} catch (JSONException e) {
e.printStackTrace();
listener.fileUploadError(imageUri, context.getString(R.string.unreadable_json));
}
}).start();
}
if(imgOutput != null) {
listener.fileUploaded(imageUri,imgOutput);
} else {
listener.fileUploadError(imageUri, context.getString(R.string.network_error));
}
}

private boolean isConnectedToInternet(Context context)
{

private boolean isConnectedToInternet(Context context) {
//verify the connectivity
ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();
if (networkInfo != null)
{
State networkState = networkInfo.getState();
if (networkState.equals(State.CONNECTED))
{
return true;
}
}
return false;
}

}
ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();
if (networkInfo != null) {
NetworkInfo.State networkState = networkInfo.getState();
if (networkState.equals(NetworkInfo.State.CONNECTED)) {
return true;
}
}
return false;
}

static class Message{
Message_Type type;
Context context;
int nbDays;
String url;
Uri imageUri;
int fileNo;
int fileNb;
}

enum Message_Type{
DELETE_IMG,
UPLOAD_IMG
}
}

+ 3
- 3
app/src/main/java/fr/mobdev/goblim/activity/LinkActivity.java View File

@@ -102,7 +102,7 @@ public class LinkActivity extends AppCompatActivity {
final NetworkAdapter listener = new NetworkAdapter() {

@Override
public void deleteSucceed()
public void deleteSucceed(String deleteUrl)
{
List<Img> images = new ArrayList<>();
images.add(image);
@@ -145,10 +145,10 @@ public class LinkActivity extends AppCompatActivity {
long days = millis / (24*60*60*1000);
//storage duration has ended or not?
if(storageDuration == 0 || storageDuration - days >= 0)
NetworkManager.getInstance(listener).deleteImage(LinkActivity.this, deleteUrl);
NetworkManager.getInstance(listener).delete(LinkActivity.this, deleteUrl);
else {
//image is no more on the server, delete is only local now
listener.deleteSucceed();
listener.deleteSucceed(deleteUrl);
}
}
})

+ 220
- 0
app/src/main/java/fr/mobdev/goblim/activity/MultiLinkActivity.java View File

@@ -0,0 +1,220 @@
/*
* Copyright (C) 2017 Anthony Chomienne, anthony@mob-dev.fr
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 3
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

package fr.mobdev.goblim.activity;

import android.content.ClipboardManager;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.Bitmap;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.widget.ImageButton;
import android.widget.Toast;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import fr.mobdev.goblim.Database;
import fr.mobdev.goblim.MultiLinkAdapter;
import fr.mobdev.goblim.NetworkManager;
import fr.mobdev.goblim.R;
import fr.mobdev.goblim.listener.NetworkAdapter;
import fr.mobdev.goblim.objects.Img;

public class MultiLinkActivity extends AppCompatActivity {

private ArrayList<String> sharedUrls;
private List<String> deleteUrls;
private MultiLinkAdapter adapter;
private List<Img> images;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.multilink);

Toolbar toolbar = (Toolbar) findViewById(R.id.link_toolbar);
setSupportActionBar(toolbar);

//get url information
Intent receiveIntent = getIntent();
Object[] extra = (Object[]) receiveIntent.getSerializableExtra("imageIds");

final Long[] ids = Arrays.copyOf(extra,extra.length,Long[].class);

//setup Adapter
adapter = new MultiLinkAdapter(ids.length);
RecyclerView listView = (RecyclerView) findViewById(R.id.link_list);
listView.setAdapter(adapter);

sharedUrls = new ArrayList<>();
deleteUrls = new ArrayList<>();
images = new ArrayList<>();

final ImageButton shareButton = (ImageButton) findViewById(R.id.share_button);
final ImageButton copyClipboardButton = (ImageButton) findViewById(R.id.copy_clipboard_button);
final ImageButton deleteImageButton = (ImageButton) findViewById(R.id.delete_button);
shareButton.setEnabled(false);
copyClipboardButton.setEnabled(false);
deleteImageButton.setEnabled(false);

//TODO Save For Rotation

new Thread(new Runnable() {
@Override
public void run() {
int i = 0;
for(Long id : ids) {
Img image = Database.getInstance(getApplicationContext()).getImage(id);
images.add(image);
String url = image.getUrl();
String shortHash = image.getShortHash();
String realShortHash = image.getRealShortHash();
String token = image.getToken();
final Bitmap bt = image.getThumb();
final int index = i;
runOnUiThread(new Runnable() {
@Override
public void run() {
adapter.setBitmap(bt,index);
}
});
//add a / at the end of the url before adding the hash
if(!url.endsWith("/"))
url = url.concat("/");
String sharedUrl = url.concat(shortHash);
String deleteUrl = url.concat("d/"+realShortHash+"/"+token);

sharedUrls.add(sharedUrl);
deleteUrls.add(deleteUrl);
i++;
}
runOnUiThread(new Runnable() {
@Override
public void run() {
shareButton.setEnabled(true);
copyClipboardButton.setEnabled(true);
deleteImageButton.setEnabled(true);
}
});

}
}).start();




//manage the sharing button
shareButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND_MULTIPLE);
List<Integer> selected = adapter.getSelecteds();
String output = "";
for(Integer index : selected) {
output += sharedUrls.get(index)+"\n";
}
sendIntent.putExtra(Intent.EXTRA_TEXT, output);
sendIntent.setType("text/plain");
startActivity(sendIntent);
}
});

//manage the clipboard button
copyClipboardButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
List<Integer> selected = adapter.getSelecteds();
String output = "";
for(Integer index : selected) {
output += sharedUrls.get(index)+"\n";
}
android.content.ClipData clip = android.content.ClipData.newPlainText("Copied URL", output);
clipboard.setPrimaryClip(clip);
Toast.makeText(MultiLinkActivity.this,getString(R.string.copy_to_clipboard),Toast.LENGTH_SHORT).show();
}
});

final NetworkAdapter listener = new NetworkAdapter() {

@Override
public void deleteSucceed(String deleteUrl)
{
List<Img> imgs = new ArrayList<>();
int idx = deleteUrls.indexOf(deleteUrl);
if(idx < 0 || idx >= images.size())
return;
imgs.add(images.get(idx));
Database.getInstance(getApplicationContext()).deleteImg(imgs);
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(MultiLinkActivity.this, R.string.delete_succeed, Toast.LENGTH_SHORT).show();
}
});
finish();
}

@Override
public void deleteError(final String error)
{
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(MultiLinkActivity.this, error, Toast.LENGTH_SHORT).show();
}
});
}
};

deleteImageButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {

AlertDialog.Builder builder = new AlertDialog.Builder(MultiLinkActivity.this);
builder.setMessage(getString(R.string.delete_selected_image))
.setCancelable(false)
.setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
List<Integer> selected = adapter.getSelecteds();
List<String> urls = new ArrayList<>();
for(Integer index : selected) {
urls.add(deleteUrls.get(index));
}
NetworkManager.getInstance(listener).deleteMultiple(MultiLinkActivity.this, urls);
}
})
.setNegativeButton(android.R.string.no, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
dialog.cancel();
}
});
AlertDialog alert = builder.create();
alert.show();
}
});
}
}

+ 286
- 115
app/src/main/java/fr/mobdev/goblim/activity/UploadActivity.java View File

@@ -19,32 +19,39 @@
package fr.mobdev.goblim.activity;

import android.app.ProgressDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Bundle;
import android.provider.OpenableColumns;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.Toolbar;
import android.util.DisplayMetrics;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.Spinner;
import android.widget.Toast;

import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import fr.mobdev.goblim.Database;
import fr.mobdev.goblim.ImageListAdapter;
import fr.mobdev.goblim.ImageLoader;
import fr.mobdev.goblim.listener.NetworkAdapter;
import fr.mobdev.goblim.NetworkManager;
import fr.mobdev.goblim.objects.Img;
@@ -60,11 +67,11 @@ import fr.mobdev.goblim.R;
public class UploadActivity extends AppCompatActivity {

private NetworkAdapter listener;
private Uri imageUri;
private ArrayList<Uri> uris;
private List<String> urls;
private List<Integer> deletedDays;
private ProgressDialog progressDialog;
private Bitmap bt;
private ImageListAdapter adapter;

//static value to handle storage durations options
private static final int NEVER = 0;
@@ -82,9 +89,10 @@ public class UploadActivity extends AppCompatActivity {
setSupportActionBar(toolbar);

setTitle(R.string.upload_pict);
hideImage();

//prepare data used for upload
imageUri = null;
uris = new ArrayList<>();
urls = new ArrayList<>();
deletedDays = new ArrayList<>();
deletedDays.add(NEVER);
@@ -92,27 +100,68 @@ public class UploadActivity extends AppCompatActivity {
deletedDays.add(SEVEN);
deletedDays.add(THIRTY);
deletedDays.add(YEAR);

updateServerList();
ImageButton resetButton = (ImageButton) findViewById(R.id.reset_button);
resetButton.setOnClickListener(new View.OnClickListener() {
ImageView imView = (ImageView) findViewById(R.id.thumbnail_main);
imView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
resetImage();
hideImage();
}
});
ProgressBar thumbProgress = (ProgressBar) findViewById(R.id.progress_thumb);
thumbProgress.setVisibility(View.GONE);

ImageListAdapter.OnImageClickListener onClickListener = new ImageListAdapter.OnImageClickListener() {
@Override
public void OnImageClick(Uri imageUri) {
displayImage(adapter.getForUri(imageUri));
}

@Override
public void onRemove(Uri imageUri) {
resetImage(imageUri);
}
};
ImageLoader.ImageLoaderListener loaderListener = new ImageLoader.ImageLoaderListener() {
@Override
public void thumbLoaded(final Uri imageUri, final Bitmap thumb) {
runOnUiThread(new Runnable() {
@Override
public void run() {
adapter.setBitmapForUri(imageUri,thumb);
}
});
}

@Override
public void allThumbLoaded() {
runOnUiThread(new Runnable() {
@Override
public void run() {
Button send = (Button) findViewById(R.id.upload_button);
send.setEnabled(true);
}
});
}
};
adapter = new ImageListAdapter(onClickListener,loaderListener);

updateServerList();
RecyclerView th = (RecyclerView) findViewById(R.id.imageList);
th.setAdapter(adapter);
Button uploadBt = (Button) findViewById(R.id.upload_button);
//retrieve previous state if it exist
if(savedInstanceState != null) {
int selectedServer = savedInstanceState.getInt("selectedServer");
imageUri = savedInstanceState.getParcelable("imageURI");
bt = savedInstanceState.getParcelable("thumb");
if(bt != null) {
ImageView th = (ImageView) findViewById(R.id.thumbnail_main);
th.setImageBitmap(bt);
} else {
displayImage();
uris = savedInstanceState.getParcelableArrayList("imageURI");
ArrayList<String> cacheUris = savedInstanceState.getStringArrayList("thumb");
List<Bitmap> bitmaps = loadFromCache(cacheUris);
if(bitmaps != null) {
adapter.addUris(this,uris,false);
adapter.setBitmapsForUris(this,uris,bitmaps);
boolean allLoaded = true;
for(Bitmap bitmap : bitmaps){
if(bitmap == null)
allLoaded = false;
}
uploadBt.setEnabled(allLoaded);
}

if (selectedServer < urls.size()) {
@@ -120,62 +169,140 @@ public class UploadActivity extends AppCompatActivity {
servers.setSelection(selectedServer);
}
}
deleteCache();

//prepare the listener that handle upload result
listener = new NetworkAdapter() {

List<Long> results = new ArrayList<>();
List<Uri> fileInError = new ArrayList<>();
List<String> errorMsg = new ArrayList<>();

@Override
public void fileUploaded(final Img image) {
public void fileUploaded(final Uri imageUri,final Img image) {
runOnUiThread(new Runnable() {
@Override
public void run() {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
bt.compress(Bitmap.CompressFormat.JPEG,70,outputStream);
adapter.getForUri(imageUri).compress(Bitmap.CompressFormat.JPEG,70,outputStream);
image.setThumbData(outputStream.toByteArray());

//add uploaded img to history
Long id = Database.getInstance(getApplicationContext()).addImage(image);
//dismiss progressDialog
if(progressDialog!=null)
progressDialog.dismiss();
resetImage();
//launch LinkActivity
Intent linkIntent = new Intent(UploadActivity.this,LinkActivity.class);
linkIntent.putExtra("imageId", id);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR);
startActivity(linkIntent);
results.add(id);
if(results.size() + fileInError.size() == uris.size())
allFileUploaded();
}
});
}

@Override
public void fileUploadError(final String error) {
public void fileUploadError(final Uri imageUri, final String error) {
fileInError.add(imageUri);
errorMsg.add(error);
if(results.size() + fileInError.size() == uris.size())
allFileUploaded();
runOnUiThread(new Runnable() {
@Override
public void run() {
//display toast error
Toast.makeText(UploadActivity.this, error, Toast.LENGTH_SHORT).show();
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR);
}
});
}

@Override
public void uploadProgress(final int progress, final int length, final int fileNo, final int nbFiles) {
runOnUiThread(new Runnable() {
@Override
public void run() {
if(progressDialog != null) {
progressDialog.dismiss();
progressDialog = null;
progressDialog.setMessage(getString(R.string.uploading_files)+" "+fileNo+"/"+nbFiles);
progressDialog.setProgress(progress * 100 / length);
}
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR);
}
});
}

@Override
public void uploadProgress(final int progress, final int length) {
public void allFileUploaded() {
runOnUiThread(new Runnable() {
@Override
public void run() {
progressDialog.setProgress(progress*100/length);
if(progressDialog != null) {
progressDialog.dismiss();
progressDialog = null;
}
if(!fileInError.isEmpty()) {
String failed = "";
String[] proj = {OpenableColumns.DISPLAY_NAME};
for(int i = 0; i < fileInError.size(); i++) {
Cursor cursor = getContentResolver().query(fileInError.get(i), proj, null, null, null);
String fileName = null;
if (cursor != null && cursor.moveToFirst()) {
fileName = cursor.getString(0);
cursor.close();
}
if(fileName == null)
fileName="File "+i;
failed += fileName;
failed += "\n\t";
failed += errorMsg.get(i);
failed += "\n";
}
AlertDialog.Builder builder = new AlertDialog.Builder(UploadActivity.this);
String message;
if(fileInError.size() > 1)
message = getString(R.string.retry_failed_uploads);
else
message = getString(R.string.retry_failed_upload);
builder.setMessage(message+"\n"+failed)
.setCancelable(false)
.setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
uploadImages(fileInError);
fileInError.clear();
errorMsg.clear();
dialog.dismiss();
}
})
.setNegativeButton(android.R.string.no, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
startLinkActivity(true);
fileInError.clear();
errorMsg.clear();
dialog.cancel();
}
});
AlertDialog alert = builder.create();
alert.show();
} else {
startLinkActivity(false);
}
}
});
}

private void startLinkActivity(boolean force) {
Intent linkIntent = null;
if(results.size() == 1 && (fileInError.isEmpty() || force)) {
//launch LinkActivity
linkIntent = new Intent(UploadActivity.this, LinkActivity.class);
linkIntent.putExtra("imageId", results.get(0));
} else if(results.size() > 1 && (fileInError.isEmpty() || force)) {
linkIntent = new Intent(UploadActivity.this,MultiLinkActivity.class);
linkIntent.putExtra("imageIds", results.toArray());
}
if(linkIntent != null) {
resetImages();
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR);
startActivity(linkIntent);
}
}
};

//prepare for upload
Button uploadBt = (Button) findViewById(R.id.upload_button);
uploadBt.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
@@ -183,36 +310,39 @@ public class UploadActivity extends AppCompatActivity {
@Override
public void run() {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_NOSENSOR);
uploadImage();
uploadImages(uris);
}
}).start();
}
});

//prepare for asking user the image he want share
Button selectBt = (Button) findViewById(R.id.select_button);
selectBt.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
requestFile();
}
});

//have we receive image from share or do you need to ask it to the user if we haven't ask for it before (screen rotation)
Intent receiveIntent = getIntent();
if((receiveIntent == null || receiveIntent.getType() == null || !receiveIntent.getType().contains("image/")) && imageUri == null) {
uploadBt.setVisibility(View.GONE);
resetButton.setVisibility(View.GONE);
if((receiveIntent == null || receiveIntent.getType() == null || !receiveIntent.getType().contains("image/")) && uris.isEmpty()) {
uploadBt.setEnabled(false);
}
else {
selectBt.setVisibility(View.GONE);
if(receiveIntent != null && imageUri == null) {
imageUri = receiveIntent.getParcelableExtra(Intent.EXTRA_STREAM);
if(receiveIntent != null && receiveIntent.getAction() != null) {
if(receiveIntent.getAction().equals(Intent.ACTION_SEND)) {
Uri imUri = receiveIntent.getParcelableExtra(Intent.EXTRA_STREAM);
uris.add(imUri);
adapter.addUri(this,imUri);
uploadBt.setEnabled(false);
} else if(receiveIntent.getAction().equals(Intent.ACTION_SEND_MULTIPLE)) {
ArrayList<Uri> imUris = receiveIntent.getParcelableArrayListExtra(Intent.EXTRA_STREAM);
if(imUris != null) {
uris.addAll(imUris);
adapter.addUris(this,imUris,true);
uploadBt.setEnabled(false);
}
}
}
displayImage();
}
updateSpanCount();
}



@Override
protected void onResume() {
super.onResume();
@@ -223,14 +353,64 @@ public class UploadActivity extends AppCompatActivity {
@Override
protected void onSaveInstanceState(Bundle savedInstanceState) {
//save imageURI and selected server position
savedInstanceState.putParcelable("imageURI", imageUri);
Spinner selectedServer = (Spinner) findViewById(R.id.servers_spinner);
int pos = selectedServer.getSelectedItemPosition();
savedInstanceState.putInt("selectedServer", pos);
savedInstanceState.putParcelable("thumb",bt);
savedInstanceState.putParcelableArrayList("imageURI", uris);
ArrayList<String> cacheUris = writesToCache(adapter.getBitmaps());
savedInstanceState.putStringArrayList("thumb",cacheUris);
super.onSaveInstanceState(savedInstanceState);
}

private ArrayList<String> writesToCache(List<Bitmap> bitmaps){
ArrayList<String> paths = new ArrayList<>();
File thumbDir = new File(getCacheDir(),"thumbs");
boolean exist = thumbDir.exists();
if(!exist) {
exist = thumbDir.mkdir();
}
if(exist)
{
for(Bitmap bt : bitmaps) {
if(bt == null) {
paths.add(null);
continue;
}
try {
File thumb = File.createTempFile("File","tmp",thumbDir);
if(thumb.exists()) {
paths.add(thumb.getPath());
FileOutputStream stream = new FileOutputStream(thumb);
bt.compress(Bitmap.CompressFormat.PNG,100,stream);
}
}catch (IOException e) {
e.printStackTrace();
}
}
}
return paths;
}

private List<Bitmap> loadFromCache(List<String> paths) {
List<Bitmap> bitmaps = new ArrayList<>();
for(String path : paths) {
if(path != null) {
Bitmap bt = BitmapFactory.decodeFile(path);
bitmaps.add(bt);
} else {
bitmaps.add(null);
}
}
return bitmaps;
}

@SuppressWarnings("ResultOfMethodCallIgnored")
private void deleteCache() {
File thumbDir = new File(getCacheDir()+"thumb");
if(thumbDir.exists())
thumbDir.delete();
}

private void updateServerList() {
Spinner serversSpinner = (Spinner) findViewById(R.id.servers_spinner);
//retrieve the selected server name in case it change his place in list
@@ -254,34 +434,21 @@ public class UploadActivity extends AppCompatActivity {
serversSpinner.setSelection(pos);
}

private void displayImage() {
if(imageUri != null) {
private void displayImage(Bitmap bt) {
if(bt != null) {
//display it in the imageView
final ProgressBar thumbProgress = (ProgressBar) findViewById(R.id.progress_thumb);
thumbProgress.setVisibility(View.VISIBLE);
final ImageView view = (ImageView) findViewById(R.id.thumbnail_main);
view.setVisibility(View.GONE);
final Button send = (Button) findViewById(R.id.upload_button);
send.setEnabled(false);
new Thread(new Runnable() {
@Override
public void run() {
bt = generateThumb(imageUri);
runOnUiThread(new Runnable() {
@Override
public void run() {
view.setVisibility(View.VISIBLE);
view.setImageBitmap(bt);
thumbProgress.setVisibility(View.GONE);
send.setEnabled(true);
}
});
}
}).start();
view.setVisibility(View.VISIBLE);
view.setImageBitmap(bt);
}
}

private void uploadImage() {
private void hideImage() {
final ImageView view = (ImageView) findViewById(R.id.thumbnail_main);
view.setVisibility(View.GONE);
}

private void uploadImages(List<Uri> imageUris) {
//what server we use
Spinner urlSpinner = (Spinner)findViewById(R.id.servers_spinner);
int pos = urlSpinner.getSelectedItemPosition();
@@ -315,20 +482,30 @@ public class UploadActivity extends AppCompatActivity {
progressDialog.show();
}
});
NetworkManager.getInstance(listener).upload(this,url, delete, imageUri);
NetworkManager.getInstance(listener).upload(this,url, delete, imageUris);
}

private void resetImage(Uri imageUri) {
uris.remove(imageUri);
ImageView view = (ImageView) findViewById(R.id.thumbnail_main);
view.setImageBitmap(null);
view.setVisibility(View.GONE);
Button bt = (Button) findViewById(R.id.upload_button);
if (bt.isEnabled() && uris.size() == 0) {
bt.setEnabled(false);
}
updateSpanCount();
}

private void resetImage(){
imageUri = null;
bt = null;
private void resetImages(){
uris.clear();
adapter.clear();
ImageView view = (ImageView) findViewById(R.id.thumbnail_main);
view.setImageBitmap(null);
ImageButton resetButton = (ImageButton) findViewById(R.id.reset_button);
resetButton.setVisibility(View.GONE);
Button selectButton = (Button) findViewById(R.id.select_button);
selectButton.setVisibility(View.VISIBLE);
Button uploadButton = (Button) findViewById(R.id.upload_button);
uploadButton.setVisibility(View.GONE);
view.setVisibility(View.GONE);
Button bt = (Button) findViewById(R.id.upload_button);
bt.setEnabled(false);
updateSpanCount();
}

private void requestFile() {
@@ -339,37 +516,27 @@ public class UploadActivity extends AppCompatActivity {

}

private Bitmap generateThumb(Uri imageUri){

Bitmap thumb = null;
try {
BitmapFactory.Options opts = new BitmapFactory.Options();
opts.inScaled = true;
opts.inDensity = 640;
opts.inTargetDensity = DisplayMetrics.DENSITY_MEDIUM;
thumb = BitmapFactory.decodeStream(getContentResolver().openInputStream(imageUri),null,opts);
System.out.println("thumb "+thumb.getWidth()+"x"+thumb.getHeight());
} catch (FileNotFoundException e) {
e.printStackTrace();
}
return thumb;
}

@Override
public void onActivityResult(int requestCode, int resultCode,Intent returnIntent) {
if(resultCode == RESULT_OK){
//retrieve uri from the request image activity and prepare
imageUri = returnIntent.getData();
Uri imageUri = returnIntent.getData();
uris.add(imageUri);
updateSpanCount();
adapter.addUri(this,imageUri);

Button uploadBt = (Button) findViewById(R.id.upload_button);
uploadBt.setVisibility(View.VISIBLE);

Button selectButton = (Button) findViewById(R.id.select_button);
selectButton.setVisibility(View.GONE);
uploadBt.setEnabled(false);
}
}

ImageButton resetButton = (ImageButton) findViewById(R.id.reset_button);
resetButton.setVisibility(View.VISIBLE);
displayImage();
private void updateSpanCount() {
RecyclerView view = (RecyclerView) findViewById(R.id.imageList);
GridLayoutManager manager = (GridLayoutManager) view.getLayoutManager();
if(uris.size() > 1) {
manager.setSpanCount(2);
} else {
manager.setSpanCount(1);
}
}

@@ -385,6 +552,10 @@ public class UploadActivity extends AppCompatActivity {
Intent newIntent = null;
if (id == R.id.action_manage_server) {
newIntent = new Intent(this,ServersActivity.class);
} else if(id == R.id.action_add_image) {
requestFile();
} else if(id == R.id.action_clear) {
resetImages();
}
if(newIntent != null)
{

+ 11
- 4
app/src/main/java/fr/mobdev/goblim/listener/NetworkAdapter.java View File

@@ -18,22 +18,24 @@

package fr.mobdev.goblim.listener;

import android.net.Uri;

import fr.mobdev.goblim.objects.Img;

public class NetworkAdapter implements NetworkListener{

@Override
public void fileUploaded(Img image) {
public void fileUploaded(Uri imageUri, Img image) {
//do nothing
}

@Override
public void fileUploadError(String error) {
public void fileUploadError(Uri imageUri, String error) {
//do nothing
}

@Override
public void deleteSucceed() {
public void deleteSucceed(String deleteUrl) {
//do nothing
}

@@ -43,7 +45,12 @@ public class NetworkAdapter implements NetworkListener{
}

@Override
public void uploadProgress(int byteWriten, int length) {
public void uploadProgress(int byteWriten, int length, int fileNo, int nbFiles) {
//do nothing
}

@Override
public void allFileUploaded() {
//do nothing
}
}

+ 7
- 4
app/src/main/java/fr/mobdev/goblim/listener/NetworkListener.java View File

@@ -18,14 +18,17 @@

package fr.mobdev.goblim.listener;

import android.net.Uri;

import java.util.EventListener;

import fr.mobdev.goblim.objects.Img;

public interface NetworkListener extends EventListener{
void fileUploaded(Img image);
void fileUploadError(String error);
void deleteSucceed();
void fileUploaded(Uri imageUri, Img image);
void fileUploadError(Uri imageUri, String error);
void deleteSucceed(String url);
void deleteError(String error);
void uploadProgress(int byteWriten, int length);
void uploadProgress(int byteWriten, int length, int fileNo, int fileNb);
void allFileUploaded();
}

+ 17
- 35
app/src/main/res/layout-land/upload.xml View File

<
@@ -42,20 +42,10 @@
android:layout_marginTop="25dp"
/>

<Button
android:layout_above="@+id/upload_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/select_pict"
android:id="@+id/select_button"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginTop="25dp"
/>
<LinearLayout
android:id="@+id/spinners_layout"
android:orientation="horizontal"
android:layout_above="@+id/select_button"
android:layout_above="@+id/upload_button"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<Spinner
@@ -88,34 +78,26 @@
/>
</LinearLayout>
</LinearLayout>
<ProgressBar
android:layout_above="@+id/spinners_layout"
android:layout_below="@+id/reset_button"
android:id="@+id/progress_thumb"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
/>
<ImageView

<FrameLayout
android:layout_alignParentTop="true"
android:layout_above="@+id/spinners_layout"
android:layout_below="@+id/reset_button"
android:id="@+id/thumbnail_main"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
/>

<ImageButton
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_marginBottom="10dp"
android:src="@android:drawable/ic_delete"
android:id="@+id/reset_button"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
/>

>
<android.support.v7.widget.RecyclerView
android:id="@+id/imageList"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layoutManager="android.support.v7.widget.GridLayoutManager"
/>
<ImageView
android:id="@+id/thumbnail_main"
android:layout_width="match_parent"