Commit 3e307bda by Lee Jaebin

ソース分離

parent 32ec4e3a
package fi.iki.elonen;
/*
* #%L
* NanoHttpd-Core
* %%
* Copyright (C) 2012 - 2015 nanohttpd
* %%
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. Neither the name of the nanohttpd nor the names of its contributors
* may be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
* #L%
*/
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.DataOutput;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.RandomAccessFile;
import java.io.UnsupportedEncodingException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.net.URLDecoder;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
import java.security.KeyStore;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.TimeZone;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.GZIPOutputStream;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLServerSocket;
import javax.net.ssl.SSLServerSocketFactory;
import javax.net.ssl.TrustManagerFactory;
import fi.iki.elonen.NanoHTTPD.Response.IStatus;
import fi.iki.elonen.NanoHTTPD.Response.Status;
/**
* A simple, tiny, nicely embeddable HTTP server in Java
* <p/>
* <p/>
* NanoHTTPD
* <p>
* Copyright (c) 2012-2013 by Paul S. Hawke, 2001,2005-2013 by Jarno Elonen,
* 2010 by Konstantinos Togias
* </p>
* <p/>
* <p/>
* <b>Features + limitations: </b>
* <ul>
* <p/>
* <li>Only one Java file</li>
* <li>Java 5 compatible</li>
* <li>Released as open source, Modified BSD licence</li>
* <li>No fixed config files, logging, authorization etc. (Implement yourself if
* you need them.)</li>
* <li>Supports parameter parsing of GET and POST methods (+ rudimentary PUT
* support in 1.25)</li>
* <li>Supports both dynamic content and file serving</li>
* <li>Supports file upload (since version 1.2, 2010)</li>
* <li>Supports partial content (streaming)</li>
* <li>Supports ETags</li>
* <li>Never caches anything</li>
* <li>Doesn't limit bandwidth, request time or simultaneous connections</li>
* <li>Default code serves files and shows all HTTP parameters and headers</li>
* <li>File server supports directory listing, index.html and index.htm</li>
* <li>File server supports partial content (streaming)</li>
* <li>File server supports ETags</li>
* <li>File server does the 301 redirection trick for directories without '/'</li>
* <li>File server supports simple skipping for files (continue download)</li>
* <li>File server serves also very long files without memory overhead</li>
* <li>Contains a built-in list of most common MIME types</li>
* <li>All header names are converted to lower case so they don't vary between
* browsers/clients</li>
* <p/>
* </ul>
* <p/>
* <p/>
* <b>How to use: </b>
* <ul>
* <p/>
* <li>Subclass and implement serve() and embed to your own program</li>
* <p/>
* </ul>
* <p/>
* See the separate "LICENSE.md" file for the distribution license (Modified BSD
* licence)
*/
public abstract class NanoHTTPD {
private long lastAccess;
static {
loadMimeTypes();
}
/**
* Pluggable strategy for asynchronously executing requests.
*/
public interface AsyncRunner {
void closeAll();
void closed(ClientHandler clientHandler);
void exec(ClientHandler code);
}
/**
* The runnable that will be used for every new client connection.
*/
public class ClientHandler implements Runnable {
private final InputStream inputStream;
private final Socket acceptSocket;
private ClientHandler(InputStream inputStream, Socket acceptSocket) {
this.inputStream = inputStream;
this.acceptSocket = acceptSocket;
}
public void close() {
safeClose(this.inputStream);
safeClose(this.acceptSocket);
}
@Override
public void run() {
OutputStream outputStream = null;
try {
outputStream = this.acceptSocket.getOutputStream();
TempFileManager tempFileManager = NanoHTTPD.this.tempFileManagerFactory.create();
HTTPSession session = new HTTPSession(tempFileManager, this.inputStream, outputStream, this.acceptSocket.getInetAddress());
while (!this.acceptSocket.isClosed()) {
session.execute();
}
} catch (Exception e) {
// When the socket is closed by the client,
// we throw our own SocketException
// to break the "keep alive" loop above. If
// the exception was anything other
// than the expected SocketException OR a
// SocketTimeoutException, print the
// stacktrace
if (!(e instanceof SocketException && "NanoHttpd Shutdown".equals(e.getMessage())) && !(e instanceof SocketTimeoutException)) {
NanoHTTPD.LOG.log(Level.FINE, "Communication with the client broken", e);
}
} finally {
safeClose(outputStream);
safeClose(this.inputStream);
safeClose(this.acceptSocket);
NanoHTTPD.this.asyncRunner.closed(this);
}
}
}
public static class Cookie {
public static String getHTTPTime(int days) {
Calendar calendar = Calendar.getInstance();
SimpleDateFormat dateFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.US);
dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
calendar.add(Calendar.DAY_OF_MONTH, days);
return dateFormat.format(calendar.getTime());
}
private final String n;
private final String v;
private final String e;
public Cookie(String name, String value) {
this(name, value, 30);
}
public Cookie(String name, String value, int numDays) {
this.n = name;
this.v = value;
this.e = getHTTPTime(numDays);
}
public Cookie(String name, String value, String expires) {
this.n = name;
this.v = value;
this.e = expires;
}
public String getHTTPHeader() {
String fmt = "%s=%s; expires=%s";
return String.format(fmt, this.n, this.v, this.e);
}
}
/**
* Provides rudimentary support for cookies. Doesn't support 'path',
* 'secure' nor 'httpOnly'. Feel free to improve it and/or add unsupported
* features.
*
* @author LordFokas
*/
public class CookieHandler implements Iterable<String> {
private final HashMap<String, String> cookies = new HashMap<String, String>();
private final ArrayList<Cookie> queue = new ArrayList<Cookie>();
public CookieHandler(Map<String, String> httpHeaders) {
String raw = httpHeaders.get("cookie");
if (raw != null) {
String[] tokens = raw.split(";");
for (String token : tokens) {
String[] data = token.trim().split("=");
if (data.length == 2) {
this.cookies.put(data[0], data[1]);
}
}
}
}
/**
* Set a cookie with an expiration date from a month ago, effectively
* deleting it on the client side.
*
* @param name
* The cookie name.
*/
public void delete(String name) {
set(name, "-delete-", -30);
}
@Override
public Iterator<String> iterator() {
return this.cookies.keySet().iterator();
}
/**
* Read a cookie from the HTTP Headers.
*
* @param name
* The cookie's name.
* @return The cookie's value if it exists, null otherwise.
*/
public String read(String name) {
return this.cookies.get(name);
}
public void set(Cookie cookie) {
this.queue.add(cookie);
}
/**
* Sets a cookie.
*
* @param name
* The cookie's name.
* @param value
* The cookie's value.
* @param expires
* How many days until the cookie expires.
*/
public void set(String name, String value, int expires) {
this.queue.add(new Cookie(name, value, Cookie.getHTTPTime(expires)));
}
/**
* Internally used by the webserver to add all queued cookies into the
* Response's HTTP Headers.
*
* @param response
* The Response object to which headers the queued cookies
* will be added.
*/
public void unloadQueue(Response response) {
for (Cookie cookie : this.queue) {
response.addHeader("Set-Cookie", cookie.getHTTPHeader());
}
}
}
/**
* Default threading strategy for NanoHTTPD.
* <p/>
* <p>
* By default, the server spawns a new Thread for every incoming request.
* These are set to <i>daemon</i> status, and named according to the request
* number. The name is useful when profiling the application.
* </p>
*/
public static class DefaultAsyncRunner implements AsyncRunner {
private long requestCount;
private final List<ClientHandler> running = Collections.synchronizedList(new ArrayList<NanoHTTPD.ClientHandler>());
/**
* @return a list with currently running clients.
*/
public List<ClientHandler> getRunning() {
return running;
}
@Override
public void closeAll() {
// copy of the list for concurrency
for (ClientHandler clientHandler : new ArrayList<ClientHandler>(this.running)) {
clientHandler.close();
}
}
@Override
public void closed(ClientHandler clientHandler) {
this.running.remove(clientHandler);
}
@Override
public void exec(ClientHandler clientHandler) {
++this.requestCount;
Thread t = new Thread(clientHandler);
t.setDaemon(true);
t.setName("NanoHttpd Request Processor (#" + this.requestCount + ")");
this.running.add(clientHandler);
t.start();
}
}
/**
* Default strategy for creating and cleaning up temporary files.
* <p/>
* <p>
* By default, files are created by <code>File.createTempFile()</code> in
* the directory specified.
* </p>
*/
public static class DefaultTempFile implements TempFile {
private final File file;
private final OutputStream fstream;
public DefaultTempFile(File tempdir) throws IOException {
this.file = File.createTempFile("NanoHTTPD-", "", tempdir);
this.fstream = new FileOutputStream(this.file);
}
@Override
public void delete() throws Exception {
safeClose(this.fstream);
if (!this.file.delete()) {
throw new Exception("could not delete temporary file");
}
}
@Override
public String getName() {
return this.file.getAbsolutePath();
}
@Override
public OutputStream open() throws Exception {
return this.fstream;
}
}
/**
* Default strategy for creating and cleaning up temporary files.
* <p/>
* <p>
* This class stores its files in the standard location (that is, wherever
* <code>java.io.tmpdir</code> points to). Files are added to an internal
* list, and deleted when no longer needed (that is, when
* <code>clear()</code> is invoked at the end of processing a request).
* </p>
*/
public static class DefaultTempFileManager implements TempFileManager {
private final File tmpdir;
private final List<TempFile> tempFiles;
public DefaultTempFileManager() {
this.tmpdir = new File(System.getProperty("java.io.tmpdir"));
if (!tmpdir.exists()) {
tmpdir.mkdirs();
}
this.tempFiles = new ArrayList<TempFile>();
}
@Override
public void clear() {
for (TempFile file : this.tempFiles) {
try {
file.delete();
} catch (Exception ignored) {
NanoHTTPD.LOG.log(Level.WARNING, "could not delete file ", ignored);
}
}
this.tempFiles.clear();
}
@Override
public TempFile createTempFile(String filename_hint) throws Exception {
DefaultTempFile tempFile = new DefaultTempFile(this.tmpdir);
this.tempFiles.add(tempFile);
return tempFile;
}
}
/**
* Default strategy for creating and cleaning up temporary files.
*/
private class DefaultTempFileManagerFactory implements TempFileManagerFactory {
@Override
public TempFileManager create() {
return new DefaultTempFileManager();
}
}
private static final String CHARSET_REGEX = "[ |\t]*(charset)[ |\t]*=[ |\t]*['|\"]?([^\"^'^;]*)['|\"]?";
private static final Pattern CHARSET_PATTERN = Pattern.compile(CHARSET_REGEX, Pattern.CASE_INSENSITIVE);
private static final String BOUNDARY_REGEX = "[ |\t]*(boundary)[ |\t]*=[ |\t]*['|\"]?([^\"^'^;]*)['|\"]?";
private static final Pattern BOUNDARY_PATTERN = Pattern.compile(BOUNDARY_REGEX, Pattern.CASE_INSENSITIVE);
/**
* Creates a normal ServerSocket for TCP connections
*/
public static class DefaultServerSocketFactory implements ServerSocketFactory {
@Override
public ServerSocket create() throws IOException {
return new ServerSocket();
}
}
/**
* Creates a new SSLServerSocket
*/
public static class SecureServerSocketFactory implements ServerSocketFactory {
private SSLServerSocketFactory sslServerSocketFactory;
private String[] sslProtocols;
public SecureServerSocketFactory(SSLServerSocketFactory sslServerSocketFactory, String[] sslProtocols) {
this.sslServerSocketFactory = sslServerSocketFactory;
this.sslProtocols = sslProtocols;
}
@Override
public ServerSocket create() throws IOException {
SSLServerSocket ss;
ss = (SSLServerSocket) this.sslServerSocketFactory.createServerSocket();
if (this.sslProtocols != null) {
ss.setEnabledProtocols(this.sslProtocols);
} else {
ss.setEnabledProtocols(ss.getSupportedProtocols());
}
ss.setUseClientMode(false);
ss.setWantClientAuth(false);
ss.setNeedClientAuth(false);
return ss;
}
}
private static final String CONTENT_DISPOSITION_REGEX = "([ |\t]*Content-Disposition[ |\t]*:)(.*)";
private static final Pattern CONTENT_DISPOSITION_PATTERN = Pattern.compile(CONTENT_DISPOSITION_REGEX, Pattern.CASE_INSENSITIVE);
private static final String CONTENT_TYPE_REGEX = "([ |\t]*content-type[ |\t]*:)(.*)";
private static final Pattern CONTENT_TYPE_PATTERN = Pattern.compile(CONTENT_TYPE_REGEX, Pattern.CASE_INSENSITIVE);
private static final String CONTENT_DISPOSITION_ATTRIBUTE_REGEX = "[ |\t]*([a-zA-Z]*)[ |\t]*=[ |\t]*['|\"]([^\"^']*)['|\"]";
private static final Pattern CONTENT_DISPOSITION_ATTRIBUTE_PATTERN = Pattern.compile(CONTENT_DISPOSITION_ATTRIBUTE_REGEX);
protected class HTTPSession implements IHTTPSession {
private static final int REQUEST_BUFFER_LEN = 512;
private static final int MEMORY_STORE_LIMIT = 1024;
public static final int BUFSIZE = 8192;
public static final int MAX_HEADER_SIZE = 1024;
private final TempFileManager tempFileManager;
private final OutputStream outputStream;
private final BufferedInputStream inputStream;
private int splitbyte;
private int rlen;
private String uri;
private Method method;
private Map<String, String> parms;
private Map<String, String> headers;
private CookieHandler cookies;
private String queryParameterString;
private String remoteIp;
private String protocolVersion;
public HTTPSession(TempFileManager tempFileManager, InputStream inputStream, OutputStream outputStream) {
this.tempFileManager = tempFileManager;
this.inputStream = new BufferedInputStream(inputStream, HTTPSession.BUFSIZE);
this.outputStream = outputStream;
}
public HTTPSession(TempFileManager tempFileManager, InputStream inputStream, OutputStream outputStream, InetAddress inetAddress) {
this.tempFileManager = tempFileManager;
this.inputStream = new BufferedInputStream(inputStream, HTTPSession.BUFSIZE);
this.outputStream = outputStream;
this.remoteIp = inetAddress.isLoopbackAddress() || inetAddress.isAnyLocalAddress() ? "127.0.0.1" : inetAddress.getHostAddress();
this.headers = new HashMap<String, String>();
}
/**
* Decodes the sent headers and loads the data into Key/value pairs
*/
private void decodeHeader(BufferedReader in, Map<String, String> pre, Map<String, String> parms, Map<String, String> headers) throws ResponseException {
headers.put("range","bytes=0-");
try {
// Read the request line
String inLine = in.readLine();
if (inLine == null) {
return;
}
StringTokenizer st = new StringTokenizer(inLine);
if (!st.hasMoreTokens()) {
throw new ResponseException(Response.Status.BAD_REQUEST, "BAD REQUEST: Syntax error. Usage: GET /example/file.html");
}
pre.put("method", st.nextToken());
if (!st.hasMoreTokens()) {
throw new ResponseException(Response.Status.BAD_REQUEST, "BAD REQUEST: Missing URI. Usage: GET /example/file.html");
}
String uri = st.nextToken();
// Decode parameters from the URI
int qmi = uri.indexOf('?');
if (qmi >= 0) {
decodeParms(uri.substring(qmi + 1), parms);
uri = decodePercent(uri.substring(0, qmi));
} else {
uri = decodePercent(uri);
}
// If there's another token, its protocol version,
// followed by HTTP headers.
// NOTE: this now forces header names lower case since they are
// case insensitive and vary by client.
if (st.hasMoreTokens()) {
protocolVersion = st.nextToken();
} else {
protocolVersion = "HTTP/1.1";
NanoHTTPD.LOG.log(Level.FINE, "no protocol version specified, strange. Assuming HTTP/1.1.");
}
String line = in.readLine();
while (line != null && line.trim().length() > 0) {
int p = line.indexOf(':');
if (p >= 0) {
headers.put(line.substring(0, p).trim().toLowerCase(Locale.US), line.substring(p + 1).trim());
}
line = in.readLine();
}
pre.put("uri", uri);
} catch (IOException ioe) {
throw new ResponseException(Response.Status.INTERNAL_ERROR, "SERVER INTERNAL ERROR: IOException: " + ioe.getMessage(), ioe);
}
}
/**
* Decodes the Multipart Body data and put it into Key/Value pairs.
*/
private void decodeMultipartFormData(String boundary, String encoding, ByteBuffer fbuf, Map<String, String> parms, Map<String, String> files) throws ResponseException {
try {
int[] boundary_idxs = getBoundaryPositions(fbuf, boundary.getBytes());
if (boundary_idxs.length < 2) {
throw new ResponseException(Response.Status.BAD_REQUEST, "BAD REQUEST: Content type is multipart/form-data but contains less than two boundary strings.");
}
byte[] part_header_buff = new byte[MAX_HEADER_SIZE];
for (int bi = 0; bi < boundary_idxs.length - 1; bi++) {
fbuf.position(boundary_idxs[bi]);
int len = (fbuf.remaining() < MAX_HEADER_SIZE) ? fbuf.remaining() : MAX_HEADER_SIZE;
fbuf.get(part_header_buff, 0, len);
BufferedReader in = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(part_header_buff, 0, len), Charset.forName(encoding)), len);
int headerLines = 0;
// First line is boundary string
String mpline = in.readLine();
headerLines++;
if (mpline == null || !mpline.contains(boundary)) {
throw new ResponseException(Response.Status.BAD_REQUEST, "BAD REQUEST: Content type is multipart/form-data but chunk does not start with boundary.");
}
String part_name = null;
String file_name = null;
String content_type = null;
// Parse the reset of the header lines
mpline = in.readLine();
headerLines++;
while (mpline != null && mpline.trim().length() > 0) {
Matcher matcher = CONTENT_DISPOSITION_PATTERN.matcher(mpline);
if (matcher.matches()) {
String attributeString = matcher.group(2);
matcher = CONTENT_DISPOSITION_ATTRIBUTE_PATTERN.matcher(attributeString);
while (matcher.find()) {
String key = matcher.group(1);
if ("name".equalsIgnoreCase(key)) {
part_name = matcher.group(2);
} else if ("filename".equalsIgnoreCase(key)) {
file_name = matcher.group(2);
}
}
}
matcher = CONTENT_TYPE_PATTERN.matcher(mpline);
if (matcher.matches()) {
content_type = matcher.group(2).trim();
}
mpline = in.readLine();
headerLines++;
}
int part_header_len = 0;
while (headerLines-- > 0) {
part_header_len = scipOverNewLine(part_header_buff, part_header_len);
}
// Read the part data
if (part_header_len >= len - 4) {
throw new ResponseException(Response.Status.INTERNAL_ERROR, "Multipart header size exceeds MAX_HEADER_SIZE.");
}
int part_data_start = boundary_idxs[bi] + part_header_len;
int part_data_end = boundary_idxs[bi + 1] - 4;
fbuf.position(part_data_start);
if (content_type == null) {
// Read the part into a string
byte[] data_bytes = new byte[part_data_end - part_data_start];
fbuf.get(data_bytes);
parms.put(part_name, new String(data_bytes, encoding));
} else {
// Read it into a file
String path = saveTmpFile(fbuf, part_data_start, part_data_end - part_data_start, file_name);
if (files.containsKey(part_name)) {
int count = 2;
while (files.containsKey(part_name + count)) {
count++;
}
files.put(part_name + count, path);
} else {
files.put(part_name, path);
}
parms.put(part_name, file_name);
}
}
} catch (ResponseException re) {
throw re;
} catch (Exception e) {
throw new ResponseException(Response.Status.INTERNAL_ERROR, e.toString());
}
}
private int scipOverNewLine(byte[] part_header_buff, int index) {
while (part_header_buff[index] != '\n') {
index++;
}
return ++index;
}
/**
* Decodes parameters in percent-encoded URI-format ( e.g.
* "name=Jack%20Daniels&pass=Single%20Malt" ) and adds them to given
* Map. NOTE: this doesn't support multiple identical keys due to the
* simplicity of Map.
*/
private void decodeParms(String parms, Map<String, String> p) {
if (parms == null) {
this.queryParameterString = "";
return;
}
this.queryParameterString = parms;
StringTokenizer st = new StringTokenizer(parms, "&");
while (st.hasMoreTokens()) {
String e = st.nextToken();
int sep = e.indexOf('=');
if (sep >= 0) {
p.put(decodePercent(e.substring(0, sep)).trim(), decodePercent(e.substring(sep + 1)));
} else {
p.put(decodePercent(e).trim(), "");
}
}
}
@Override
public void execute() throws IOException {
Response r = null;
try {
// Read the first 8192 bytes.
// The full header should fit in here.
// Apache's default header limit is 8KB.
// Do NOT assume that a single read will get the entire header
// at once!
byte[] buf = new byte[HTTPSession.BUFSIZE];
this.splitbyte = 0;
this.rlen = 0;
int read;
this.inputStream.mark(HTTPSession.BUFSIZE);
try {
read = this.inputStream.read(buf, 0, HTTPSession.BUFSIZE);
} catch (Exception e) {
safeClose(this.inputStream);
safeClose(this.outputStream);
throw new SocketException("NanoHttpd Shutdown");
}
if (read == -1) {
// socket was been closed
safeClose(this.inputStream);
safeClose(this.outputStream);
throw new SocketException("NanoHttpd Shutdown");
}
while (read > 0) {
this.rlen += read;
this.splitbyte = findHeaderEnd(buf, this.rlen);
if (this.splitbyte > 0) {
break;
}
read = this.inputStream.read(buf, this.rlen, HTTPSession.BUFSIZE - this.rlen);
}
if (this.splitbyte < this.rlen) {
this.inputStream.reset();
this.inputStream.skip(this.splitbyte);
}
this.parms = new HashMap<String, String>();
if (null == this.headers) {
this.headers = new HashMap<String, String>();
} else {
this.headers.clear();
}
// Create a BufferedReader for parsing the header.
BufferedReader hin = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(buf, 0, this.rlen)));
// Decode the header into parms and header java properties
Map<String, String> pre = new HashMap<String, String>();
decodeHeader(hin, pre, this.parms, this.headers);
if (null != this.remoteIp) {
this.headers.put("remote-addr", this.remoteIp);
this.headers.put("http-client-ip", this.remoteIp);
}
this.method = Method.lookup(pre.get("method"));
if (this.method == null) {
throw new ResponseException(Response.Status.BAD_REQUEST, "BAD REQUEST: Syntax error.");
}
this.uri = pre.get("uri");
this.cookies = new CookieHandler(this.headers);
String connection = this.headers.get("connection");
boolean keepAlive = "HTTP/1.1".equals(protocolVersion) && (connection == null || !connection.matches("(?i).*close.*"));
// Ok, now do the serve()
// TODO: long body_size = getBodySize();
// TODO: long pos_before_serve = this.inputStream.totalRead()
// (requires implementaion for totalRead())
r = serve(this);
// TODO: this.inputStream.skip(body_size -
// (this.inputStream.totalRead() - pos_before_serve))
if (r == null) {
throw new ResponseException(Response.Status.INTERNAL_ERROR, "SERVER INTERNAL ERROR: Serve() returned a null response.");
} else {
String acceptEncoding = this.headers.get("accept-encoding");
this.cookies.unloadQueue(r);
r.setRequestMethod(this.method);
r.setGzipEncoding(useGzipWhenAccepted(r) && acceptEncoding != null && acceptEncoding.contains("gzip"));
r.setKeepAlive(keepAlive);
r.send(this.outputStream);
}
if (!keepAlive || "close".equalsIgnoreCase(r.getHeader("connection"))) {
throw new SocketException("NanoHttpd Shutdown");
}
} catch (SocketException e) {
// throw it out to close socket object (finalAccept)
throw e;
} catch (SocketTimeoutException ste) {
// treat socket timeouts the same way we treat socket exceptions
// i.e. close the stream & finalAccept object by throwing the
// exception up the call stack.
throw ste;
} catch (IOException ioe) {
Response resp = newFixedLengthResponse(Response.Status.INTERNAL_ERROR, NanoHTTPD.MIME_PLAINTEXT, "SERVER INTERNAL ERROR: IOException: " + ioe.getMessage());
resp.send(this.outputStream);
safeClose(this.outputStream);
} catch (ResponseException re) {
Response resp = newFixedLengthResponse(re.getStatus(), NanoHTTPD.MIME_PLAINTEXT, re.getMessage());
resp.send(this.outputStream);
safeClose(this.outputStream);
} finally {
safeClose(r);
this.tempFileManager.clear();
}
}
/**
* Find byte index separating header from body. It must be the last byte
* of the first two sequential new lines.
*/
private int findHeaderEnd(final byte[] buf, int rlen) {
int splitbyte = 0;
while (splitbyte + 1 < rlen) {
// RFC2616
if (buf[splitbyte] == '\r' && buf[splitbyte + 1] == '\n' && splitbyte + 3 < rlen && buf[splitbyte + 2] == '\r' && buf[splitbyte + 3] == '\n') {
return splitbyte + 4;
}
// tolerance
if (buf[splitbyte] == '\n' && buf[splitbyte + 1] == '\n') {
return splitbyte + 2;
}
splitbyte++;
}
return 0;
}
/**
* Find the byte positions where multipart boundaries start. This reads
* a large block at a time and uses a temporary buffer to optimize
* (memory mapped) file access.
*/
private int[] getBoundaryPositions(ByteBuffer b, byte[] boundary) {
int[] res = new int[0];
if (b.remaining() < boundary.length) {
return res;
}
int search_window_pos = 0;
byte[] search_window = new byte[4 * 1024 + boundary.length];
int first_fill = (b.remaining() < search_window.length) ? b.remaining() : search_window.length;
b.get(search_window, 0, first_fill);
int new_bytes = first_fill - boundary.length;
do {
// Search the search_window
for (int j = 0; j < new_bytes; j++) {
for (int i = 0; i < boundary.length; i++) {
if (search_window[j + i] != boundary[i]) {
break;
}
if (i == boundary.length - 1) {
// Match found, add it to results
int[] new_res = new int[res.length + 1];
System.arraycopy(res, 0, new_res, 0, res.length);
new_res[res.length] = search_window_pos + j;
res = new_res;
}
}
}
search_window_pos += new_bytes;
// Copy the end of the buffer to the start
System.arraycopy(search_window, search_window.length - boundary.length, search_window, 0, boundary.length);
// Refill search_window
new_bytes = search_window.length - boundary.length;
new_bytes = (b.remaining() < new_bytes) ? b.remaining() : new_bytes;
b.get(search_window, boundary.length, new_bytes);
} while (new_bytes > 0);
return res;
}
@Override
public CookieHandler getCookies() {
return this.cookies;
}
@Override
public final Map<String, String> getHeaders() {
return this.headers;
}
@Override
public final InputStream getInputStream() {
return this.inputStream;
}
@Override
public final Method getMethod() {
return this.method;
}
@Override
public final Map<String, String> getParms() {
return this.parms;
}
@Override
public String getQueryParameterString() {
return this.queryParameterString;
}
private RandomAccessFile getTmpBucket() {
try {
TempFile tempFile = this.tempFileManager.createTempFile(null);
return new RandomAccessFile(tempFile.getName(), "rw");
} catch (Exception e) {
throw new Error(e); // we won't recover, so throw an error
}
}
@Override
public final String getUri() {
return this.uri;
}
/**
* Deduce body length in bytes. Either from "content-length" header or
* read bytes.
*/
public long getBodySize() {
if (this.headers.containsKey("content-length")) {
return Long.parseLong(this.headers.get("content-length"));
} else if (this.splitbyte < this.rlen) {
return this.rlen - this.splitbyte;
}
return 0;
}
@Override
public void parseBody(Map<String, String> files) throws IOException, ResponseException {
RandomAccessFile randomAccessFile = null;
try {
long size = getBodySize();
ByteArrayOutputStream baos = null;
DataOutput request_data_output;
// Store the request in memory or a file, depending on size
if (size < MEMORY_STORE_LIMIT) {
baos = new ByteArrayOutputStream();
request_data_output = new DataOutputStream(baos);
} else {
randomAccessFile = getTmpBucket();
request_data_output = randomAccessFile;
}
// Read all the body and write it to request_data_output
byte[] buf = new byte[REQUEST_BUFFER_LEN];
while (this.rlen >= 0 && size > 0) {
this.rlen = this.inputStream.read(buf, 0, (int) Math.min(size, REQUEST_BUFFER_LEN));
size -= this.rlen;
if (this.rlen > 0) {
request_data_output.write(buf, 0, this.rlen);
}
}
ByteBuffer fbuf;
if (baos != null) {
fbuf = ByteBuffer.wrap(baos.toByteArray(), 0, baos.size());
} else {
fbuf = randomAccessFile.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, randomAccessFile.length());
randomAccessFile.seek(0);
}
// If the method is POST, there may be parameters
// in data section, too, read it:
if (Method.POST.equals(this.method)) {
String contentType = "";
String contentTypeHeader = this.headers.get("content-type");
StringTokenizer st = null;
if (contentTypeHeader != null) {
st = new StringTokenizer(contentTypeHeader, ",; ");
if (st.hasMoreTokens()) {
contentType = st.nextToken();
}
}
if ("multipart/form-data".equalsIgnoreCase(contentType)) {
// Handle multipart/form-data
if (!st.hasMoreTokens()) {
throw new ResponseException(Response.Status.BAD_REQUEST,
"BAD REQUEST: Content type is multipart/form-data but boundary missing. Usage: GET /example/file.html");
}
decodeMultipartFormData(getAttributeFromContentHeader(contentTypeHeader, BOUNDARY_PATTERN, null), //
getAttributeFromContentHeader(contentTypeHeader, CHARSET_PATTERN, "US-ASCII"), fbuf, this.parms, files);
} else {
byte[] postBytes = new byte[fbuf.remaining()];
fbuf.get(postBytes);
String postLine = new String(postBytes).trim();
// Handle application/x-www-form-urlencoded
if ("application/x-www-form-urlencoded".equalsIgnoreCase(contentType)) {
decodeParms(postLine, this.parms);
} else if (postLine.length() != 0) {
// Special case for raw POST data => create a
// special files entry "postData" with raw content
// data
files.put("postData", postLine);
}
}
} else if (Method.PUT.equals(this.method)) {
files.put("content", saveTmpFile(fbuf, 0, fbuf.limit(), null));
}
} finally {
safeClose(randomAccessFile);
}
}
private String getAttributeFromContentHeader(String contentTypeHeader, Pattern pattern, String defaultValue) {
Matcher matcher = pattern.matcher(contentTypeHeader);
return matcher.find() ? matcher.group(2) : defaultValue;
}
/**
* Retrieves the content of a sent file and saves it to a temporary
* file. The full path to the saved file is returned.
*/
private String saveTmpFile(ByteBuffer b, int offset, int len, String filename_hint) {
String path = "";
if (len > 0) {
FileOutputStream fileOutputStream = null;
try {
TempFile tempFile = this.tempFileManager.createTempFile(filename_hint);
ByteBuffer src = b.duplicate();
fileOutputStream = new FileOutputStream(tempFile.getName());
FileChannel dest = fileOutputStream.getChannel();
src.position(offset).limit(offset + len);
dest.write(src.slice());
path = tempFile.getName();
} catch (Exception e) { // Catch exception if any
throw new Error(e); // we won't recover, so throw an error
} finally {
safeClose(fileOutputStream);
}
}
return path;
}
}
/**
* Handles one session, i.e. parses the HTTP request and returns the
* response.
*/
public interface IHTTPSession {
void execute() throws IOException;
CookieHandler getCookies();
Map<String, String> getHeaders();
InputStream getInputStream();
Method getMethod();
Map<String, String> getParms();
String getQueryParameterString();
/**
* @return the path part of the URL.
*/
String getUri();
/**
* Adds the files in the request body to the files map.
*
* @param files
* map to modify
*/
void parseBody(Map<String, String> files) throws IOException, ResponseException;
}
/**
* HTTP Request methods, with the ability to decode a <code>String</code>
* back to its enum value.
*/
public enum Method {
GET,
PUT,
POST,
DELETE,
HEAD,
OPTIONS,
TRACE,
CONNECT,
PATCH;
static Method lookup(String method) {
for (Method m : Method.values()) {
if (m.toString().equalsIgnoreCase(method)) {
return m;
}
}
return null;
}
}
/**
* HTTP response. Return one of these from serve().
*/
public static class Response implements Closeable {
public interface IStatus {
String getDescription();
int getRequestStatus();
}
/**
* Some HTTP response status codes
*/
public enum Status implements IStatus {
SWITCH_PROTOCOL(101, "Switching Protocols"),
OK(200, "OK"),
CREATED(201, "Created"),
ACCEPTED(202, "Accepted"),
NO_CONTENT(204, "No Content"),
PARTIAL_CONTENT(206, "Partial Content"),
REDIRECT(301, "Moved Permanently"),
NOT_MODIFIED(304, "Not Modified"),
BAD_REQUEST(400, "Bad Request"),
UNAUTHORIZED(401, "Unauthorized"),
FORBIDDEN(403, "Forbidden"),
NOT_FOUND(404, "Not Found"),
METHOD_NOT_ALLOWED(405, "Method Not Allowed"),
NOT_ACCEPTABLE(406, "Not Acceptable"),
REQUEST_TIMEOUT(408, "Request Timeout"),
CONFLICT(409, "Conflict"),
RANGE_NOT_SATISFIABLE(416, "Requested Range Not Satisfiable"),
INTERNAL_ERROR(500, "Internal Server Error"),
NOT_IMPLEMENTED(501, "Not Implemented"),
UNSUPPORTED_HTTP_VERSION(505, "HTTP Version Not Supported");
private final int requestStatus;
private final String description;
Status(int requestStatus, String description) {
this.requestStatus = requestStatus;
this.description = description;
}
@Override
public String getDescription() {
return "" + this.requestStatus + " " + this.description;
}
@Override
public int getRequestStatus() {
return this.requestStatus;
}
}
/**
* Output stream that will automatically send every write to the wrapped
* OutputStream according to chunked transfer:
* http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.6.1
*/
private static class ChunkedOutputStream extends FilterOutputStream {
public ChunkedOutputStream(OutputStream out) {
super(out);
}
@Override
public void write(int b) throws IOException {
byte[] data = {
(byte) b
};
write(data, 0, 1);
}
@Override
public void write(byte[] b) throws IOException {
write(b, 0, b.length);
}
@Override
public void write(byte[] b, int off, int len) throws IOException {
if (len == 0) {
return;
}
out.write(String.format("%x\r\n", len).getBytes());
out.write(b, off, len);
out.write("\r\n".getBytes());
}
public void finish() throws IOException {
out.write("0\r\n\r\n".getBytes());
}
}
/**
* HTTP status code after processing, e.g. "200 OK", Status.OK
*/
private IStatus status;
/**
* MIME type of content, e.g. "text/html"
*/
private String mimeType;
/**
* Data of the response, may be null.
*/
private InputStream data;
private long contentLength;
/**
* Headers for the HTTP response. Use addHeader() to add lines.
*/
private final Map<String, String> header = new HashMap<String, String>();
/**
* The request method that spawned this response.
*/
private Method requestMethod;
/**
* Use chunkedTransfer
*/
private boolean chunkedTransfer;
private boolean encodeAsGzip;
private boolean keepAlive;
/**
* Creates a fixed length response if totalBytes>=0, otherwise chunked.
*/
protected Response(IStatus status, String mimeType, InputStream data, long totalBytes) {
this.status = status;
this.mimeType = mimeType;
if (data == null) {
this.data = new ByteArrayInputStream(new byte[0]);
this.contentLength = 0L;
} else {
this.data = data;
this.contentLength = totalBytes;
}
this.chunkedTransfer = this.contentLength < 0;
keepAlive = true;
}
@Override
public void close() throws IOException {
if (this.data != null) {
this.data.close();
}
}
/**
* Adds given line to the header.
*/
public void addHeader(String name, String value) {
this.header.put(name, value);
}
public InputStream getData() {
return this.data;
}
public String getHeader(String name) {
for (String headerName : header.keySet()) {
if (headerName.equalsIgnoreCase(name)) {
return header.get(headerName);
}
}
return null;
}
public String getMimeType() {
return this.mimeType;
}
public Method getRequestMethod() {
return this.requestMethod;
}
public IStatus getStatus() {
return this.status;
}
public void setGzipEncoding(boolean encodeAsGzip) {
this.encodeAsGzip = encodeAsGzip;
}
public void setKeepAlive(boolean useKeepAlive) {
this.keepAlive = useKeepAlive;
}
private static boolean headerAlreadySent(Map<String, String> header, String name) {
boolean alreadySent = false;
for (String headerName : header.keySet()) {
alreadySent |= headerName.equalsIgnoreCase(name);
}
return alreadySent;
}
/**
* Sends given response to the socket.
*/
protected void send(OutputStream outputStream) {
String mime = this.mimeType;
SimpleDateFormat gmtFrmt = new SimpleDateFormat("E, d MMM yyyy HH:mm:ss 'GMT'", Locale.US);
gmtFrmt.setTimeZone(TimeZone.getTimeZone("GMT"));
try {
if (this.status == null) {
throw new Error("sendResponse(): Status can't be null.");
}
PrintWriter pw = new PrintWriter(new BufferedWriter(new OutputStreamWriter(outputStream, "UTF-8")), false);
pw.print("HTTP/1.1 " + this.status.getDescription() + " \r\n");
if (mime != null) {
pw.print("Content-Type: " + mime + "\r\n");
}
if (this.header == null || this.header.get("Date") == null) {
pw.print("Date: " + gmtFrmt.format(new Date()) + "\r\n");
}
if (this.header != null) {
for (String key : this.header.keySet()) {
String value = this.header.get(key);
pw.print(key + ": " + value + "\r\n");
}
}
if (!headerAlreadySent(header, "connection")) {
pw.print("Connection: " + (this.keepAlive ? "keep-alive" : "close") + "\r\n");
}
if (headerAlreadySent(this.header, "content-length")) {
encodeAsGzip = false;
}
if (encodeAsGzip) {
pw.print("Content-Encoding: gzip\r\n");
setChunkedTransfer(true);
}
long pending = this.data != null ? this.contentLength : 0;
if (this.requestMethod != Method.HEAD && this.chunkedTransfer) {
pw.print("Transfer-Encoding: chunked\r\n");
} else if (!encodeAsGzip) {
pending = sendContentLengthHeaderIfNotAlreadyPresent(pw, this.header, pending);
}
pw.print("\r\n");
pw.flush();
sendBodyWithCorrectTransferAndEncoding(outputStream, pending);
outputStream.flush();
safeClose(this.data);
} catch (SocketException se) {
NanoHTTPD.LOG.log(Level.WARNING, "SocketException", se.getLocalizedMessage());
} catch (IOException ioe) {
NanoHTTPD.LOG.log(Level.SEVERE, "Could not send response to the client", ioe);
}
}
private void sendBodyWithCorrectTransferAndEncoding(OutputStream outputStream, long pending) throws IOException {
if (this.requestMethod != Method.HEAD && this.chunkedTransfer) {
ChunkedOutputStream chunkedOutputStream = new ChunkedOutputStream(outputStream);
sendBodyWithCorrectEncoding(chunkedOutputStream, -1);
chunkedOutputStream.finish();
} else {
sendBodyWithCorrectEncoding(outputStream, pending);
}
}
private void sendBodyWithCorrectEncoding(OutputStream outputStream, long pending) throws IOException {
if (encodeAsGzip) {
GZIPOutputStream gzipOutputStream = new GZIPOutputStream(outputStream);
sendBody(gzipOutputStream, -1);
gzipOutputStream.finish();
} else {
sendBody(outputStream, pending);
}
}
/**
* Sends the body to the specified OutputStream. The pending parameter
* limits the maximum amounts of bytes sent unless it is -1, in which
* case everything is sent.
*
* @param outputStream
* the OutputStream to send data to
* @param pending
* -1 to send everything, otherwise sets a max limit to the
* number of bytes sent
* @throws IOException
* if something goes wrong while sending the data.
*/
private void sendBody(OutputStream outputStream, long pending) throws IOException {
long BUFFER_SIZE = 16 * 1024;
byte[] buff = new byte[(int) BUFFER_SIZE];
boolean sendEverything = pending == -1;
while (pending > 0 || sendEverything) {
long bytesToRead = sendEverything ? BUFFER_SIZE : Math.min(pending, BUFFER_SIZE);
int read = this.data.read(buff, 0, (int) bytesToRead);
if (read <= 0) {
break;
}
outputStream.write(buff, 0, read);
if (!sendEverything) {
pending -= read;
}
}
}
protected static long sendContentLengthHeaderIfNotAlreadyPresent(PrintWriter pw, Map<String, String> header, long size) {
for (String headerName : header.keySet()) {
if ("content-length".equalsIgnoreCase(headerName)) {
try {
return Long.parseLong(header.get(headerName));
} catch (NumberFormatException ex) {
return size;
}
}
}
pw.print("Content-Length: " + size + "\r\n");
return size;
}
public void setChunkedTransfer(boolean chunkedTransfer) {
this.chunkedTransfer = chunkedTransfer;
}
public void setData(InputStream data) {
this.data = data;
}
public void setMimeType(String mimeType) {
this.mimeType = mimeType;
}
public void setRequestMethod(Method requestMethod) {
this.requestMethod = requestMethod;
}
public void setStatus(IStatus status) {
this.status = status;
}
}
public static final class ResponseException extends Exception {
private static final long serialVersionUID = 6569838532917408380L;
private final Response.Status status;
public ResponseException(Response.Status status, String message) {
super(message);
this.status = status;
}
public ResponseException(Response.Status status, String message, Exception e) {
super(message, e);
this.status = status;
}
public Response.Status getStatus() {
return this.status;
}
}
/**
* The runnable that will be used for the main listening thread.
*/
public class ServerRunnable implements Runnable {
private final int timeout;
private IOException bindException;
private boolean hasBinded = false;
private ServerRunnable(int timeout) {
this.timeout = timeout;
}
@Override
public void run() {
try {
myServerSocket.bind(hostname != null ? new InetSocketAddress(hostname, myPort) : new InetSocketAddress(myPort));
hasBinded = true;
} catch (IOException e) {
this.bindException = e;
return;
}
do {
try {
final Socket finalAccept = NanoHTTPD.this.myServerSocket.accept();
if (this.timeout > 0) {
finalAccept.setSoTimeout(this.timeout);
}
final InputStream inputStream = finalAccept.getInputStream();
lastAccess = System.currentTimeMillis();
NanoHTTPD.this.asyncRunner.exec(createClientHandler(finalAccept, inputStream));
} catch (IOException e) {
NanoHTTPD.LOG.log(Level.FINE, "Communication with the client broken", e);
}
} while (!NanoHTTPD.this.myServerSocket.isClosed());
}
}
public long getLastAccess() {
return lastAccess;
}
/**
* A temp file.
* <p/>
* <p>
* Temp files are responsible for managing the actual temporary storage and
* cleaning themselves up when no longer needed.
* </p>
*/
public interface TempFile {
public void delete() throws Exception;
public String getName();
public OutputStream open() throws Exception;
}
/**
* Temp file manager.
* <p/>
* <p>
* Temp file managers are created 1-to-1 with incoming requests, to create
* and cleanup temporary files created as a result of handling the request.
* </p>
*/
public interface TempFileManager {
void clear();
public TempFile createTempFile(String filename_hint) throws Exception;
}
/**
* Factory to create temp file managers.
*/
public interface TempFileManagerFactory {
public TempFileManager create();
}
/**
* Factory to create ServerSocketFactories.
*/
public interface ServerSocketFactory {
public ServerSocket create() throws IOException;
}
/**
* Maximum time to wait on Socket.getInputStream().read() (in milliseconds)
* This is required as the Keep-Alive HTTP connections would otherwise block
* the socket reading thread forever (or as long the browser is open).
*/
public static final int SOCKET_READ_TIMEOUT = 5000;
/**
* Common MIME type for dynamic content: plain text
*/
public static final String MIME_PLAINTEXT = "text/plain";
/**
* Common MIME type for dynamic content: html
*/
public static final String MIME_HTML = "text/html";
/**
* Pseudo-Parameter to use to store the actual query string in the
* parameters map for later re-processing.
*/
private static final String QUERY_STRING_PARAMETER = "NanoHttpd.QUERY_STRING";
/**
* logger to log to.
*/
private static final Logger LOG = Logger.getLogger(NanoHTTPD.class.getName());
/**
* Hashtable mapping (String)FILENAME_EXTENSION -> (String)MIME_TYPE
*/
protected static Map<String, String> MIME_TYPES;
private static void loadMimeTypes() {
if (MIME_TYPES == null) {
MIME_TYPES = new HashMap<String, String>();
MIME_TYPES.put("css", "text/css");
MIME_TYPES.put("htm", "text/html");
MIME_TYPES.put("html", "text/html");
MIME_TYPES.put("xml", "text/xml");
MIME_TYPES.put("java", "text/x-java-source, text/java");
MIME_TYPES.put("md", "text/plain");
MIME_TYPES.put("txt", "text/plain");
MIME_TYPES.put("asc", "text/plain");
MIME_TYPES.put("gif", "image/gif");
MIME_TYPES.put("jpg", "image/jpeg");
MIME_TYPES.put("jpeg", "image/jpeg");
MIME_TYPES.put("png", "image/png");
MIME_TYPES.put("svg", "image/svg+xml");
MIME_TYPES.put("mp3", "audio/mpeg");
MIME_TYPES.put("m3u", "audio/mpeg-url");
MIME_TYPES.put("mp4", "video/mp4");
MIME_TYPES.put("ogv", "video/ogg");
MIME_TYPES.put("flv", "video/x-flv");
MIME_TYPES.put("mov", "video/quicktime");
MIME_TYPES.put("swf", "application/x-shockwave-flash");
MIME_TYPES.put("js", "application/javascript");
MIME_TYPES.put("pdf", "application/pdf");
MIME_TYPES.put("doc", "application/msword");
MIME_TYPES.put("ogg", "application/x-ogg");
MIME_TYPES.put("zip", "application/octet-stream");
MIME_TYPES.put("exe", "application/octet-stream");
MIME_TYPES.put("class", "application/octet-stream");
MIME_TYPES.put("m3u8", "application/vnd.apple.mpegurl");
MIME_TYPES.put("ts", "video/mp2t");
if (MIME_TYPES.isEmpty()) {
LOG.log(Level.WARNING, "no mime types found in the classpath! please provide mimetypes.properties");
}
}
}
/**
* Creates an SSLSocketFactory for HTTPS. Pass a loaded KeyStore and an
* array of loaded KeyManagers. These objects must properly
* loaded/initialized by the caller.
*/
public static SSLServerSocketFactory makeSSLSocketFactory(KeyStore loadedKeyStore, KeyManager[] keyManagers) throws IOException {
SSLServerSocketFactory res;
try {
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(loadedKeyStore);
SSLContext ctx = SSLContext.getInstance("TLS");
ctx.init(keyManagers, trustManagerFactory.getTrustManagers(), null);
res = ctx.getServerSocketFactory();
} catch (Exception e) {
throw new IOException(e.getMessage());
}
return res;
}
/**
* Creates an SSLSocketFactory for HTTPS. Pass a loaded KeyStore and a
* loaded KeyManagerFactory. These objects must properly loaded/initialized
* by the caller.
*/
public static SSLServerSocketFactory makeSSLSocketFactory(KeyStore loadedKeyStore, KeyManagerFactory loadedKeyFactory) throws IOException {
try {
return makeSSLSocketFactory(loadedKeyStore, loadedKeyFactory.getKeyManagers());
} catch (Exception e) {
throw new IOException(e.getMessage());
}
}
/**
* Creates an SSLSocketFactory for HTTPS. Pass a KeyStore resource with your
* certificate and passphrase
*/
public static SSLServerSocketFactory makeSSLSocketFactory(String keyAndTrustStoreClasspathPath, char[] passphrase) throws IOException {
try {
KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
InputStream keystoreStream = NanoHTTPD.class.getResourceAsStream(keyAndTrustStoreClasspathPath);
keystore.load(keystoreStream, passphrase);
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyManagerFactory.init(keystore, passphrase);
return makeSSLSocketFactory(keystore, keyManagerFactory);
} catch (Exception e) {
throw new IOException(e.getMessage());
}
}
/**
* Get MIME type from file name extension, if possible
*
* @param uri
* the string representing a file
* @return the connected mime/type
*/
public static String getMimeTypeForFile(String uri) {
int dot = uri.lastIndexOf('.');
String mime = null;
if (dot >= 0) {
mime = MIME_TYPES.get(uri.substring(dot + 1).toLowerCase());
}
return mime == null ? "application/octet-stream" : mime;
}
private static final void safeClose(Object closeable) {
try {
if (closeable != null) {
if (closeable instanceof Closeable) {
((Closeable) closeable).close();
} else if (closeable instanceof Socket) {
((Socket) closeable).close();
} else if (closeable instanceof ServerSocket) {
((ServerSocket) closeable).close();
} else {
throw new IllegalArgumentException("Unknown object to close");
}
}
} catch (IOException e) {
NanoHTTPD.LOG.log(Level.SEVERE, "Could not close", e);
}
}
private final String hostname;
private final int myPort;
private volatile ServerSocket myServerSocket;
private ServerSocketFactory serverSocketFactory = new DefaultServerSocketFactory();
private Thread myThread;
/**
* Pluggable strategy for asynchronously executing requests.
*/
protected AsyncRunner asyncRunner;
/**
* Pluggable strategy for creating and cleaning up temporary files.
*/
private TempFileManagerFactory tempFileManagerFactory;
/**
* Constructs an HTTP server on given port.
*/
public NanoHTTPD(int port) {
this(null, port);
}
// -------------------------------------------------------------------------------
// //
//
// Threading Strategy.
//
// -------------------------------------------------------------------------------
// //
/**
* Constructs an HTTP server on given hostname and port.
*/
public NanoHTTPD(String hostname, int port) {
this.hostname = hostname;
this.myPort = port;
setTempFileManagerFactory(new DefaultTempFileManagerFactory());
setAsyncRunner(new DefaultAsyncRunner());
}
/**
* Forcibly closes all connections that are open.
*/
public synchronized void closeAllConnections() {
stop();
}
/**
* create a instance of the client handler, subclasses can return a subclass
* of the ClientHandler.
*
* @param finalAccept
* the socket the cleint is connected to
* @param inputStream
* the input stream
* @return the client handler
*/
protected ClientHandler createClientHandler(final Socket finalAccept, final InputStream inputStream) {
return new ClientHandler(inputStream, finalAccept);
}
/**
* Instantiate the server runnable, can be overwritten by subclasses to
* provide a subclass of the ServerRunnable.
*
* @param timeout
* the socet timeout to use.
* @return the server runnable.
*/
protected ServerRunnable createServerRunnable(final int timeout) {
return new ServerRunnable(timeout);
}
/**
* Decode parameters from a URL, handing the case where a single parameter
* name might have been supplied several times, by return lists of values.
* In general these lists will contain a single element.
*
* @param parms
* original <b>NanoHTTPD</b> parameters values, as passed to the
* <code>serve()</code> method.
* @return a map of <code>String</code> (parameter name) to
* <code>List&lt;String&gt;</code> (a list of the values supplied).
*/
protected static Map<String, List<String>> decodeParameters(Map<String, String> parms) {
return decodeParameters(parms.get(NanoHTTPD.QUERY_STRING_PARAMETER));
}
// -------------------------------------------------------------------------------
// //
/**
* Decode parameters from a URL, handing the case where a single parameter
* name might have been supplied several times, by return lists of values.
* In general these lists will contain a single element.
*
* @param queryString
* a query string pulled from the URL.
* @return a map of <code>String</code> (parameter name) to
* <code>List&lt;String&gt;</code> (a list of the values supplied).
*/
protected static Map<String, List<String>> decodeParameters(String queryString) {
Map<String, List<String>> parms = new HashMap<String, List<String>>();
if (queryString != null) {
StringTokenizer st = new StringTokenizer(queryString, "&");
while (st.hasMoreTokens()) {
String e = st.nextToken();
int sep = e.indexOf('=');
String propertyName = sep >= 0 ? decodePercent(e.substring(0, sep)).trim() : decodePercent(e).trim();
if (!parms.containsKey(propertyName)) {
parms.put(propertyName, new ArrayList<String>());
}
String propertyValue = sep >= 0 ? decodePercent(e.substring(sep + 1)) : null;
if (propertyValue != null) {
parms.get(propertyName).add(propertyValue);
}
}
}
return parms;
}
/**
* Decode percent encoded <code>String</code> values.
*
* @param str
* the percent encoded <code>String</code>
* @return expanded form of the input, for example "foo%20bar" becomes
* "foo bar"
*/
protected static String decodePercent(String str) {
String decoded = null;
try {
decoded = URLDecoder.decode(str, "UTF8");
} catch (UnsupportedEncodingException ignored) {
NanoHTTPD.LOG.log(Level.WARNING, "Encoding not supported, ignored", ignored);
}
return decoded;
}
/**
* @return true if the gzip compression should be used if the client
* accespts it. Default this option is on for text content and off
* for everything. Override this for custom semantics.
*/
protected boolean useGzipWhenAccepted(Response r) {
return r.getMimeType() != null && r.getMimeType().toLowerCase().contains("text/");
}
public final int getListeningPort() {
return this.myServerSocket == null ? -1 : this.myServerSocket.getLocalPort();
}
public final boolean isAlive() {
return wasStarted() && !this.myServerSocket.isClosed() && this.myThread.isAlive();
}
public ServerSocketFactory getServerSocketFactory() {
return serverSocketFactory;
}
public void setServerSocketFactory(ServerSocketFactory serverSocketFactory) {
this.serverSocketFactory = serverSocketFactory;
}
public String getHostname() {
return hostname;
}
public TempFileManagerFactory getTempFileManagerFactory() {
return tempFileManagerFactory;
}
/**
* Call before start() to serve over HTTPS instead of HTTP
*/
public void makeSecure(SSLServerSocketFactory sslServerSocketFactory, String[] sslProtocols) {
this.serverSocketFactory = new SecureServerSocketFactory(sslServerSocketFactory, sslProtocols);
}
/**
* Create a response with unknown length (using HTTP 1.1 chunking).
*/
public static Response newChunkedResponse(IStatus status, String mimeType, InputStream data) {
return new Response(status, mimeType, data, -1);
}
/**
* Create a response with known length.
*/
public static Response newFixedLengthResponse(IStatus status, String mimeType, InputStream data, long totalBytes) {
return new Response(status, mimeType, data, totalBytes);
}
/**
* Create a text response with known length.
*/
public static Response newFixedLengthResponse(IStatus status, String mimeType, String txt) {
if (txt == null) {
return newFixedLengthResponse(status, mimeType, new ByteArrayInputStream(new byte[0]), 0);
} else {
byte[] bytes;
try {
bytes = txt.getBytes("UTF-8");
} catch (UnsupportedEncodingException e) {
NanoHTTPD.LOG.log(Level.SEVERE, "encoding problem, responding nothing", e);
bytes = new byte[0];
}
return newFixedLengthResponse(status, mimeType, new ByteArrayInputStream(bytes), bytes.length);
}
}
/**
* Create a text response with known length.
*/
public static Response newFixedLengthResponse(String msg) {
return newFixedLengthResponse(Status.OK, NanoHTTPD.MIME_HTML, msg);
}
/**
* Override this to customize the server.
* <p/>
* <p/>
* (By default, this returns a 404 "Not Found" plain text error response.)
*
* @param session
* The HTTP session
* @return HTTP response, see class Response for details
*/
public Response serve(IHTTPSession session) {
Map<String, String> files = new HashMap<String, String>();
Method method = session.getMethod();
if (Method.PUT.equals(method) || Method.POST.equals(method)) {
try {
session.parseBody(files);
} catch (IOException ioe) {
return newFixedLengthResponse(Response.Status.INTERNAL_ERROR, NanoHTTPD.MIME_PLAINTEXT, "SERVER INTERNAL ERROR: IOException: " + ioe.getMessage());
} catch (ResponseException re) {
return newFixedLengthResponse(re.getStatus(), NanoHTTPD.MIME_PLAINTEXT, re.getMessage());
}
}
Map<String, String> parms = session.getParms();
parms.put(NanoHTTPD.QUERY_STRING_PARAMETER, session.getQueryParameterString());
return serve(session.getUri(), method, session.getHeaders(), parms, files);
}
/**
* Override this to customize the server.
* <p/>
* <p/>
* (By default, this returns a 404 "Not Found" plain text error response.)
*
* @param uri
* Percent-decoded URI without parameters, for example
* "/index.cgi"
* @param method
* "GET", "POST" etc.
* @param parms
* Parsed, percent decoded parameters from URI and, in case of
* POST, data.
* @param headers
* Header entries, percent decoded
* @return HTTP response, see class Response for details
*/
@Deprecated
public Response serve(String uri, Method method, Map<String, String> headers, Map<String, String> parms, Map<String, String> files) {
return newFixedLengthResponse(Response.Status.NOT_FOUND, NanoHTTPD.MIME_PLAINTEXT, "Not Found");
}
/**
* Pluggable strategy for asynchronously executing requests.
*
* @param asyncRunner
* new strategy for handling threads.
*/
public void setAsyncRunner(AsyncRunner asyncRunner) {
this.asyncRunner = asyncRunner;
}
/**
* Pluggable strategy for creating and cleaning up temporary files.
*
* @param tempFileManagerFactory
* new strategy for handling temp files.
*/
public void setTempFileManagerFactory(TempFileManagerFactory tempFileManagerFactory) {
this.tempFileManagerFactory = tempFileManagerFactory;
}
/**
* Start the server.
*
* @throws IOException
* if the socket is in use.
*/
public void start() throws IOException {
start(NanoHTTPD.SOCKET_READ_TIMEOUT);
}
/**
* Starts the server (in setDaemon(true) mode).
*/
public void start(final int timeout) throws IOException {
start(timeout, true);
}
/**
* Start the server.
*
* @param timeout
* timeout to use for socket connections.
* @param daemon
* start the thread daemon or not.
* @throws IOException
* if the socket is in use.
*/
public void start(final int timeout, boolean daemon) throws IOException {
this.myServerSocket = this.getServerSocketFactory().create();
this.myServerSocket.setReuseAddress(true);
ServerRunnable serverRunnable = createServerRunnable(timeout);
this.myThread = new Thread(serverRunnable);
this.myThread.setDaemon(daemon);
this.myThread.setName("NanoHttpd Main Listener");
this.myThread.start();
while (!serverRunnable.hasBinded && serverRunnable.bindException == null) {
try {
Thread.sleep(10L);
} catch (Throwable e) {
// on android this may not be allowed, that's why we
// catch throwable the wait should be very short because we are
// just waiting for the bind of the socket
}
}
if (serverRunnable.bindException != null) {
throw serverRunnable.bindException;
}
}
/**
* Stop the server.
*/
public void stop() {
try {
safeClose(this.myServerSocket);
this.asyncRunner.closeAll();
if (this.myThread != null) {
this.myThread.join();
}
} catch (Exception e) {
NanoHTTPD.LOG.log(Level.SEVERE, "Could not stop all connections", e);
}
}
public final boolean wasStarted() {
return this.myServerSocket != null && this.myThread != null;
}
}
\ No newline at end of file
......@@ -146,10 +146,6 @@ public class Constant {
}
public interface editionType {
int BIZ = 1;
int SIGNAGE = 2;
int AD = 3;
int READER = 4;
int CHECK = 5;
}
}
......@@ -96,4 +96,25 @@ public class ABookKeys {
public static final int HTTP_STATUS_SUCCESS = 200;
public static final String HELP_VIEW_TYPE = "helpViewType";
public static final String ABOOK = "abook";
public static final String MOVEPAGE = "/abookmovepage";
public static final String DETAILLOG = "/abookdetaillog";
public static final String CONTENTLINK = "/abookcontentlink";
public static final String NEXTCONTENT = "/abooknextcontent";
public static final String SEND_CUSTOM_LOG = "/abooksendcustomlog";
public static final String GETITS_OPEN_SHARE = "/abookopengetitsshare";
public static final String PING = "/ping";
public static final String CMD_GETITS_OPEN_SHARE = "opengetitsshare";
public static final String CMD_GETITS_CLOSE_SHARE = "closegetitsshare";
public static final String PARAM_LAST_PAGE_NUMBER = "lastPageNo";
public static final String PARAM_LAST_PAGE_START_DATE = "lastPageStartDate";
public static final String PARAM_READING_LOG_ID = "readingLogId";
public static final String PARAM_SHARE_URL = "shareUrl";
public static final String URI = "uri";
public static final String CLOSE = "CLOSE";
public static final String OK = "OK";
}
......@@ -15,7 +15,7 @@ public class DatabaseVersions {
public static final int Plus_1_3_0 = 11; // 2014/01/27 ABook+1.3
public static final int Plus_1_4_0_OLD = 12; // 2014/04/1 ABook+1.4旧
public static final int Plus_1_4_0 = 20; // 2014/05/21 ABook+1.4
public static final int Plus_1_4_3 = 30; // 2014/05/21 ABook+1.4.3
public static final int Plus_1_4_3 = 30; // 2014/05/21 ABook+1.4.3
public static final int Plus_1_5_0 = 40; // 2014/07/25 ABook+1.5
public static final int Plus_1_5_2 = 50; // 2014/07/25 ABook+1.5.2
public static final int Plus_1_6_0 = 60; // 2014/12/01 ABook+1.6.0
......
......@@ -1082,7 +1082,7 @@ public class ContentDao extends AbstractDao {
sql.append(" ON tc.content_id = rcc.content_id ");
sql.append(" INNER JOIN m_category AS mc ");
sql.append(" ON rcc.category_relation_id = mc.category_relation_id ");
sql.append(" WHERE tc.common_content_flg = 1");
sql.append(" WHERE tc.common_content_flg = 1");
sql.append(" ORDER BY mc.category_id, tc.content_id DESC ");
return rawQueryGetDtoList(sql.toString(), null, ContentDto.class);
......
......@@ -95,10 +95,6 @@ public class ProjectContentDao extends AbstractDao {
}
}
public void deleteAll() {
delete("r_project_content", null, null);
}
/**
* リストを削除
* @param projectId
......
package jp.agentec.abook.abv.bl.download;
import java.io.File;
import java.net.MalformedURLException;
import java.util.Locale;
import jp.agentec.abook.abv.bl.acms.client.AcmsClient;
import jp.agentec.abook.abv.bl.acms.client.json.reader.ReaderContentJSON;
import jp.agentec.abook.abv.bl.acms.client.json.reader.ReaderContentVersionJSON;
import jp.agentec.abook.abv.bl.acms.client.parameters.ContentDownloadLogParameters;
import jp.agentec.abook.abv.bl.acms.type.ContentZipType;
import jp.agentec.abook.abv.bl.acms.type.DownloadStatusType;
import jp.agentec.abook.abv.bl.common.ABVEnvironment;
import jp.agentec.abook.abv.bl.common.Constant.ReaderConstant;
import jp.agentec.abook.abv.bl.common.exception.ABVException;
import jp.agentec.abook.abv.bl.common.exception.ABVExceptionCode;
import jp.agentec.abook.abv.bl.common.exception.AcmsException;
import jp.agentec.abook.abv.bl.common.exception.NetworkDisconnectedException;
import jp.agentec.abook.abv.bl.common.log.Logger;
import jp.agentec.abook.abv.bl.common.util.SecurityUtil;
import jp.agentec.abook.abv.bl.data.ABVDataCache;
import jp.agentec.abook.abv.bl.data.dao.AbstractDao;
import jp.agentec.abook.abv.bl.data.dao.ContentDao;
import jp.agentec.abook.abv.bl.data.dao.ContentIdConvDao;
import jp.agentec.abook.abv.bl.dto.ContentDto;
import jp.agentec.abook.abv.bl.dto.ContentIdConvDto;
import jp.agentec.abook.abv.bl.logic.AbstractLogic;
import jp.agentec.abook.abv.bl.logic.ContentLogic;
import jp.agentec.adf.net.http.HttpDownloadState;
import jp.agentec.adf.net.http.HttpFileDownloader;
import jp.agentec.adf.net.http.HttpResponse;
import jp.agentec.adf.util.DateTimeUtil;
import jp.agentec.adf.util.FileUtil;
import jp.agentec.adf.util.StringUtil;
public class ReaderContentDownloader {
private static final String TAG = "ReaderContentDownloader";
private static ReaderContentDownloader instance;
private ContentDownloader contentDownloader = ContentDownloader.getInstance();
private ContentLogic contentLogic = AbstractLogic.getLogic(ContentLogic.class);
private ContentDao contentDao = AbstractDao.getDao(ContentDao.class);
private ContentFileExtractor contentFileExtractor = ContentFileExtractor.getInstance();
private ABVDataCache cache = ABVDataCache.getInstance();
private ReaderContentDownloader() {
}
/**
* {@link ContentDownloader} クラスのインスタンスを取得します。
*
* @return {@link ContentDownloader} クラスのインスタンスを返します。
* @since 1.0.0
*/
public static ReaderContentDownloader getInstance() {
if (instance == null) {
synchronized (ContentDownloader.class) {
if (instance == null) {
instance = new ReaderContentDownloader();
}
}
}
return instance;
}
/**
* ReaderコンテンツのDLを実行する
*
* @param contentDto
* @return
* @throws AcmsException
*/
HttpFileDownloader executeDownload(ContentDto contentDto) throws AcmsException {
ContentIdConvDto contentIdConvDto = AbstractDao.getDao(ContentIdConvDao.class).getDto(contentDto.contentId);
String keyForCms = SecurityUtil.getEncryptString(ReaderConstant.SHARED_SECRET_CMS, "" + System.currentTimeMillis(), true);
String downloadUrl = contentIdConvDto.downloadUrl + "?contentId=" + contentIdConvDto.cmsContentId + "&filePattern=2&key=" + keyForCms;
HttpFileDownloader downloader = contentDownloader.executeDownload(contentDto, downloadUrl);
// ログ送信
ContentDownloadLogParameters downloadParam = new ContentDownloadLogParameters(null, contentDto.contentId, DateTimeUtil.getCurrentTimestamp()
, contentDto.resourceVersion, ABVEnvironment.DeviceTypeId, DownloadStatusType.Downloading);
try {
AcmsClient acmsClient = AcmsClient.getInstance(cache.getUrlPath(), ABVEnvironment.getInstance().networkAdapter);
acmsClient.contentDownloadLog(downloadParam);
} catch (NetworkDisconnectedException e) { // サーバ側のエラーはここでは無視
Logger.e(TAG, "contentDownloadLog " + downloadParam, e);
}
return downloader;
}
/**
* コンテンツバージョン情報を取得する
*
* @param contentVersionUrl
* @param cmsContentId
* @return
* @throws NetworkDisconnectedException
* @throws AcmsException
*/
public ReaderContentVersionJSON getContentVersion(String contentVersionUrl, long cmsContentId) throws AcmsException, NetworkDisconnectedException {
String keyForCms = SecurityUtil.getEncryptString(ReaderConstant.SHARED_SECRET_CMS, "" + System.currentTimeMillis(), true);
String url = contentVersionUrl + "?contentId=" + cmsContentId + "&key=" + keyForCms + "&language=" + Locale.getDefault().getLanguage();
HttpResponse response = AcmsClient.getInstance(ABVEnvironment.getInstance().networkAdapter).send(url , "", null);
return new ReaderContentVersionJSON(response.httpResponseBody);
}
/**
* Reader用のコンテンツダウンロード(一連)
*
* @param contentJson
* @throws Exception
*/
public void downloadContent(ReaderContentJSON contentJson) throws Exception {
downloadContent(contentJson.siteId, contentJson.cmsContentId, contentJson.downloadUrl, contentJson.contractId, contentJson.contractContentId, contentJson.shortUrl);
}
public void registrationDownloadContent(ReaderContentJSON contentJson) throws Exception {
long contentId = downloadContentInforAndContentDetail(contentJson.siteId, contentJson.cmsContentId, contentJson.downloadUrl, contentJson.contractId, contentJson.contractContentId, contentJson.shortUrl);
contentDownloader.reserveDownload(contentId, false);
}
//コンテンツ情報とコンテンツ詳細をダウンロードして該当するコンテンツIDに返す
private long downloadContentInforAndContentDetail(int siteId, long cmsContentId, String downloadUrl, int contractId, int contractContentId, String shortUrl) throws Exception {
long contentId = ContentLogic.getReaderContentId(siteId, cmsContentId);
String keyForCms = SecurityUtil.getEncryptString(ReaderConstant.SHARED_SECRET_CMS, "" + System.currentTimeMillis(), true);
// info (Sync)
String url = downloadUrl + "?contentId=" + cmsContentId + "&filePattern=1&key=" + keyForCms;
downloadContentInfo(contentId , url);
if (contractContentId == -1){
// ダウンロード開始時点でContractContentIdが分からない場合、ダウンロードした情報で上書き
contractContentId = contentDao.getContent(contentId).contractContentId;
}
// contentIdConv登録
ContentIdConvDto contentIdConvDto = new ContentIdConvDto(contentId, siteId, contractId, cmsContentId, contractContentId, downloadUrl, shortUrl);
AbstractDao.getDao(ContentIdConvDao.class).insertOrUpdateupdate(contentIdConvDto);
// detail (Sync) コンテンツ詳細はPDFコンテンツのみダウンロードする
ContentDto contentDto = contentDao.getContent(contentId);
if (contentDto != null && contentDto.isPdf()) {
url = downloadUrl + "?contentId=" + cmsContentId + "&filePattern=3&key=" + keyForCms;
downloadContentDetail(contentId , url);
}
return contentId;
}
public void downloadContent(int siteId, long cmsContentId, String downloadUrl, int contractId, int contractContentId, String shortUrl) throws Exception {
long contentId = downloadContentInforAndContentDetail(siteId, cmsContentId, downloadUrl, contractId, contractContentId, shortUrl);
// downloadキューへ登録 (ASync)
contentDownloader.download(contentId);
}
/**
* Reader用ContentInfoダウンロード
*
* @param contentId
* @param downloadUrl
* @throws NetworkDisconnectedException
* @throws ABVException
* @throws Exception
*/
public void downloadContentInfo(long contentId, String downloadUrl) throws Exception {
String outputFilePath = String.format("%s/%s_%s.zip", ABVEnvironment.getInstance().getContentDownloadsDirectoryPath(), ContentZipType.ContentInfo.name(), contentId);
ContentRefresher.getInstance().addRefreshingContentId(contentId);
Logger.d(TAG, "[url : %s], [contentId : %s]", downloadUrl, contentId);
HttpFileDownloader downloader = new HttpFileDownloader(downloadUrl, null, outputFilePath);
downloader.downloadSync();
if (downloader.getState() == HttpDownloadState.failed) {
throw downloader.getError();
}
else if (downloader.getState() != HttpDownloadState.finished) { // ほぼ原因はNW切断
Logger.e(TAG, "download failed. [url : %s], [contentId : %s] state=%s", downloadUrl, contentId, downloader.getState());
throw new NetworkDisconnectedException();
}
try {
String contentPath = ABVEnvironment.getInstance().getContentDirectoryPath(contentId, false) + File.separator;
FileUtil.deleteFileOnly(contentPath);
contentFileExtractor.removeContentCash(contentId);
contentFileExtractor.extractZipFile(contentId, outputFilePath, contentPath);
contentLogic.saveContentInfo(contentPath, contentId, false, false);
} catch (Exception e) {
Logger.e("Can't extract zip file. detail : ", e);
} finally {
// ダウンロードしていたファイルの削除
FileUtil.delete(outputFilePath);
}
}
/**
* Reader用ContentDetailダウンロード
*
* @param contentId
* @param downloadUrl
* @throws MalformedURLException
* @throws InterruptedException
* @throws ABVException
*/
public void downloadContentDetail(long contentId, String downloadUrl)
throws MalformedURLException, InterruptedException, ABVException {
String outputFilePath = String.format("%s/%s_%s.zip", ABVEnvironment.getInstance().getContentDownloadsDirectoryPath(), ContentZipType.ContentDetail, contentId);
Logger.d(TAG, "call api : getContentForAndroid [getContentInfoReader] " + downloadUrl + " outputFilePath=" + outputFilePath);
String fileName = null;
String outputFileName = null;
try {
HttpFileDownloader downloader = new HttpFileDownloader(downloadUrl, null, outputFilePath);
downloader.downloadSync();
if (downloader.getState() != HttpDownloadState.finished) { // finished以外は失敗とみなす(pausedの場合はgetErrorはnull)
throw new ABVException(ABVExceptionCode.C_E_CONTENT_1002, downloader.getError());
}
String contentPath = ABVEnvironment.getInstance().getContentDetailDirectoryPath(contentId, false);
FileUtil.delete(contentPath);
contentFileExtractor.extractZipFile(contentId, downloader.getOutputFileName(), contentPath);
fileName = contentPath;
outputFileName = downloader.getOutputFileName();
contentLogic.saveContentDetail(contentId, contentPath);
} catch (Exception e) {
Logger.e(TAG, "downloadContentDetailForReader failed", e);
if (!StringUtil.isNullOrWhiteSpace(fileName)) {
FileUtil.delete(fileName);
}
if (!StringUtil.isNullOrWhiteSpace(outputFileName)) {
FileUtil.delete(outputFileName);
}
}
}
}
......@@ -36,7 +36,6 @@ public class ContractLogic extends AbstractLogic {
private static final String True = "Y";
private ServiceOptionDao serviceOptionDao = AbstractDao.getDao(ServiceOptionDao.class);
private AcmsDao acmsDao = AbstractDao.getDao(AcmsDao.class);
private ContractDao contractDao = AbstractDao.getDao(ContractDao.class);
/**
* {@link ContractLogic} クラスのインスタンスを初期化します。
......@@ -77,31 +76,6 @@ public class ContractLogic extends AbstractLogic {
}
/**
* ABookMaker専用 。
* @throws Exception
* @since 1.0.0
*/
public void insertServiceOption(List<ServiceOptionDto> list) {
if (list != null && list.size() > 0) {
try {
serviceOptionDao.beginTransaction();
serviceOptionDao.deleteServiceOptions();
serviceOptionDao.insertServiceOptions(list);
serviceOptionDao.commit();
cache.addServiceOptions(list);
} catch (Exception e) {
serviceOptionDao.rollback();
throw new RuntimeException(e);
} finally {
}
}
}
/**
* 権限喪失,非公開,削除時のDL済コンテンツ強制削除するかどうかを示します。
* @return 権限喪失,非公開,削除時のDL済コンテンツ強制削除する場合、trueを返します。
* @since 1.0.0
......@@ -448,80 +422,4 @@ public class ContractLogic extends AbstractLogic {
public static boolean getBooleanValue(String value) {
return True.equals(value);
}
public ContractDto getContractByContentId(long contentId) {
ContentIdConvDto contentIdConvDto = AbstractDao.getDao(ContentIdConvDao.class).getDto(contentId);
if (contentIdConvDto == null) {
Logger.e(TAG, "contentId not found in ContentIdConv" + contentId);
return null;
}
return contractDao.getDto(contentIdConvDto.siteId, contentIdConvDto.contractId);
}
/**
* 事業者のプッシュメッセージ設定を更新します<br>
* Repository Serverに更新情報を送信します
*
* @param repositoryFqdn
* @param registractionId
* @param contractDto
* @throws AcmsException
* @throws NetworkDisconnectedException
*/
public void updateReceivePushMessage(String repositoryFqdn, String registractionId, ContractDto contractDto) throws AcmsException, NetworkDisconnectedException {
try {
contractDao.beginTransaction();
contractDto.userUpdateDate = DateTimeUtil.getCurrentDate();
contractDao.update(contractDto);
// プッシュメッセージを受信する事業者の情報を送信する
List<ContractDto> permitContractList = contractDao.findByReceivePushMessage(ReceivePushMessage.PERMIT);
RepoClient.getInstance().sendReaderUserAccount(repositoryFqdn, registractionId, permitContractList);
contractDao.commit();
} catch (AcmsException e) {
contractDao.rollback();
throw e;
} catch (NetworkDisconnectedException e) {
contractDao.rollback();
throw e;
}
}
/**
* 事業者のプッシュメッセージ設定を更新します<br>
* Repository Serverに更新情報を送信します
*
* @param repositoryFqdn
* @param registractionId
* @param contractDtoList
* @throws AcmsException
* @throws NetworkDisconnectedException
*/
public void updateReceivePushMessage(String repositoryFqdn, String registractionId, List<ContractDto> contractDtoList) throws AcmsException, NetworkDisconnectedException {
try {
ArrayList<ContractDto> permitContractList = new ArrayList<ContractDto>();
for (ContractDto contractDto : contractDtoList) {
if (contractDto.receivePushMessage == ReceivePushMessage.PERMIT) {
permitContractList.add(contractDto);
}
}
// プッシュメッセージを受信する事業者の情報を送信する
RepoClient.getInstance().sendReaderUserAccount(repositoryFqdn, registractionId, permitContractList);
contractDao.beginTransaction();
for (ContractDto contractDto : contractDtoList) {
contractDto.userUpdateDate = DateTimeUtil.getCurrentDate();
contractDao.update(contractDto);
}
contractDao.commit();
} catch (AcmsException e) {
contractDao.rollback();
throw e;
} catch (NetworkDisconnectedException e) {
contractDao.rollback();
throw e;
}
}
}
......@@ -192,64 +192,10 @@ public class ProjectLogic extends AbstractLogic {
}
mPushMessageDao.delete(deleteProjectDto.projectId);
mProjectDao.delete(deleteProjectDto);
if (deleteProjectDto.contentId != null) {
ContentDto contentDto = mContentDao.getContent(deleteProjectDto.contentId);
if (contentDto != null) {
AbstractLogic.getLogic(ContentLogic.class).deleteContent(contentDto, true);
}
}
String projectTaskDirPath = ABVEnvironment.getInstance().getProjectDirFile(deleteProjectDto.projectId);
FileUtil.delete(projectTaskDirPath);
}
// for (ProjectDto serverProjectDto : serverProjects) {
//
//
// int localProjectIndex = localProjects.indexOf(serverProjectDto);
// if (localProjectIndex >= 0) {
// ProjectDto localDto = localProjects.get(localProjectIndex);
// if (!serverProjectDto.equalsLastEdit(localDto)) {
// serverProjectDto.needSyncFlg = true;
// mProjectContentDao.deleteProjectContent(serverProjectDto.projectId);
// for (ProjectContentDto projectContentDto : serverProjectDto.projectContentDtoList) {
// mProjectContentDao.insertProjectContent(projectContentDto);
// }
// mProjectDao.update(serverProjectDto);
// }
// localProjects.remove(localDto);
// } else {
// // プロジェクトの登録
// serverProjectDto.needSyncFlg = true;
// mProjectDao.insert(serverProjectDto);
// // 関連資料の登録
// for (ProjectContentDto projectContentDto : serverProjectDto.projectContentDtoList) {
// mProjectContentDao.insertProjectContent(projectContentDto);
// }
// }
//
// for (PushMessageDto dto : serverProjectDto.pushMessageList) {
// mPushMessageDao.insert(dto);
// }
// }
//
// // サーバーから取得したプロジェクト情報がローカルに存在しないので削除する
// for (ProjectDto localProjectDto : localProjects) {
// List<TaskDto> taskDtoList = mTaskDao.selectAllTaskByProjectId(localProjectDto.projectId);
// for(TaskDto taskDto : taskDtoList) {
// mTaskDao.delete(taskDto);
// }
// mPushMessageDao.delete(localProjectDto.projectId);
// mProjectDao.delete(localProjectDto);
// if (localProjectDto.contentId != null) {
// ContentDto contentDto = mContentDao.getContent(localProjectDto.contentId);
// if (contentDto != null) {
// AbstractLogic.getLogic(ContentLogic.class).deleteContent(contentDto, true);
// }
// }
// String projectTaskDirPath = ABVEnvironment.getInstance().getProjectDirFile(localProjectDto.projectId);
// FileUtil.delete(projectTaskDirPath);
// }
}
/**
......
package jp.agentec.abook.abv.bl.net;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
import jp.agentec.abook.abv.bl.common.Callback;
import jp.agentec.abook.abv.bl.common.log.Logger;
import jp.agentec.adf.net.http.SimpleWebServer;
/**
* 360°コンテンツ表示用WebServer
*
* Created by tsukada on 2016/02/12.
*/
public class PanoServer extends SimpleWebServer {
private static final java.lang.String TAG = "PanoServer";
public static final String ABOOK = "abook";
public static final String MOVEPAGE = "/abookmovepage";
public static final String DETAILLOG = "/abookdetaillog";
public static final String CONTENTLINK = "/abookcontentlink";
public static final String NEXTCONTENT = "/abooknextcontent";
public static final String SEND_CUSTOM_LOG = "/abooksendcustomlog";
public static final String GETITS_OPEN_SHARE = "/abookopengetitsshare";
public static final String PING = "/ping";
public static final String CMD_GETITS_OPEN_SHARE = "opengetitsshare";
public static final String CMD_GETITS_CLOSE_SHARE = "closegetitsshare";
public static final String PARAM_LAST_PAGE_NUMBER = "lastPageNo";
public static final String PARAM_LAST_PAGE_START_DATE = "lastPageStartDate";
public static final String PARAM_READING_LOG_ID = "readingLogId";
public static final String PARAM_SHARE_URL = "shareUrl";
public static final String URI = "uri";
public static final String CLOSE = "CLOSE";
public static final String OK = "OK";
public static final int MAX_NO_ACCESS_TIME = 60000; // pingがこれ以上の間来ない場合にコンテンツ閲覧終了とみなす(ミリ秒)
private Callback callback;
private Long contentId;
private Thread panoCheckThread;
private boolean isCloseWaiting = false;
public PanoServer(String host, int port, Long contentId, String wwwroot, boolean quiet, boolean extAccess, List<String> userAgentList, Callback callback) {
super(host, port, wwwroot, quiet, extAccess, userAgentList);
this.contentId = contentId;
this.callback = callback;
panoCheckThread = new Thread() { // チェックスレッド(リクエストが一定時間以上ない場合に終了させる)
@Override
public void run() {
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
break;
}
if (System.currentTimeMillis() - getLastAccess() > PanoServer.MAX_NO_ACCESS_TIME) {
callStop();
break;
}
}
}
};
}
@Override
public void start(final int timeout, boolean daemon) throws IOException {
super.start(timeout, daemon);
panoCheckThread.start();
}
@Override
protected String modifyUri(String uri) throws Exception {
if (!uri.startsWith("/" + contentId + "/")) {
Logger.e(TAG, "invalid uri. server=" + getHostname());
throw new Exception("You are not allowed to access.");
}
uri = uri.substring(("/" + contentId).length());
return super.modifyUri(uri);
}
@Override
protected Response getResponse(String uri, Map<String, String> params, Map<String, String> headers, IHTTPSession session) {
if (isCloseWaiting()) {
return newFixedLengthResponse(CLOSE);
}
else if (uri.startsWith(PING)) {
return newFixedLengthResponse(OK);
}
else if (uri.startsWith(MOVEPAGE) || uri.startsWith(DETAILLOG) || uri.startsWith(CONTENTLINK)) {
params.put(URI, uri);
Object ret = callback.callback(params);
if (ret != null) {
return newFixedLengthResponse(ret.toString());
}
return newFixedLengthResponse(OK);
}
else {
return super.getResponse(uri, params, headers, session);
}
}
public void callStop() {
if (!isCloseWaiting()) {
isCloseWaiting = true;
callback.callback(contentId);
new Timer().schedule(new TimerTask() {
@Override
public void run() {
PanoServer.this.stop();
}
}, 30000L);
}
if (panoCheckThread != null) {
panoCheckThread.interrupt();
panoCheckThread = null;
}
}
public boolean isCloseWaiting() {
return isCloseWaiting;
}
}
\ No newline at end of file
......@@ -137,33 +137,6 @@ public class RepoClient {
AbstractDao.getDao(EnqueteDao.class).deleteUnsendEnquete(contentListStr);
}
/**
* ShareKeyを解決し、コンテンツ情報・ダウンロード先を取得する
*
* @param repositoryFqdn
* @param shareKey
* @return
* @throws NetworkDisconnectedException
* @throws AcmsException
*/
public ReaderContentJSON resolveShareKey(String repositoryFqdn, String shareKey, String password) throws NetworkDisconnectedException, AcmsException {
String keyForReader = SecurityUtil.getEncryptString(ReaderConstant.SHARED_SECRET_REPO, "" + System.currentTimeMillis(), true);
String url = "https://" + repositoryFqdn + "/repo/getDlUrl?key="+ keyForReader + "&shareKey=" + shareKey
+ (StringUtil.isNullOrEmpty(password)?"":"&password="+password) + "&language=" + Locale.getDefault().getLanguage() + "&checkOnly=n";
HttpResponse response = AcmsClient.getInstance(ABVEnvironment.getInstance().networkAdapter).send(url , "", null);
Logger.d(TAG, "resolveShareKey response=%s", response.httpResponseBody);
return new ReaderContentJSON(response.httpResponseBody, "http://" + repositoryFqdn + "/" + shareKey);
}
public ReaderContentJSON resolveShareKeyCheckOnly(String repositoryFqdn, String shareKey, String password) throws NetworkDisconnectedException, AcmsException {
String keyForReader = SecurityUtil.getEncryptString(ReaderConstant.SHARED_SECRET_REPO, "" + System.currentTimeMillis(), true);
String url = "https://" + repositoryFqdn + "/repo/getDlUrl?key="+ keyForReader + "&shareKey=" + shareKey
+ (StringUtil.isNullOrEmpty(password)?"":"&password="+password) + "&language=" + Locale.getDefault().getLanguage() + "&checkOnly=y";
HttpResponse response = AcmsClient.getInstance(ABVEnvironment.getInstance().networkAdapter).send(url , "", null);
Logger.d(TAG, "resolveShareKey response=%s", response.httpResponseBody);
return new ReaderContentJSON(response.httpResponseBody, "http://" + repositoryFqdn + "/" + shareKey);
}
public AcmsCommonJSON sendReaderUserAccount(String repositoryFqdn, String registractionId, List<ContractDto> permitContractList) throws AcmsException, NetworkDisconnectedException {
// 送信
StringBuilder sb = new StringBuilder();
......
package jp.agentec.adf.net.http;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import jp.agentec.abook.abv.bl.common.log.Logger;
import fi.iki.elonen.NanoHTTPD;
import fi.iki.elonen.NanoHTTPD.Response.IStatus;
public class SimpleWebServer extends NanoHTTPD {
private static final String TAG = "SimpleWebServer";
private final boolean quiet;
private boolean extAccess;
private List<String> userAgentList;
protected File rootDir;
private String firstUserAgent;
public SimpleWebServer(String host, int port, String wwwroot, boolean quiet, boolean extAccess, List<String> userAgentList) {
super(host, port);
this.quiet = quiet;
this.extAccess = extAccess;
this.userAgentList = userAgentList;
this.rootDir = new File(wwwroot);
}
protected Response getForbiddenResponse(String s) {
return newFixedLengthResponse(Response.Status.FORBIDDEN, NanoHTTPD.MIME_PLAINTEXT, "FORBIDDEN: " + s);
}
protected Response getInternalErrorResponse(String s) {
return newFixedLengthResponse(Response.Status.INTERNAL_ERROR, NanoHTTPD.MIME_PLAINTEXT, "INTERNAL ERROR: " + s);
}
protected Response getNotFoundResponse() {
return newFixedLengthResponse(Response.Status.NOT_FOUND, NanoHTTPD.MIME_PLAINTEXT, "Error 404, file not found.");
}
public static Response newFixedLengthResponse(IStatus status, String mimeType, String message) {
Response response = NanoHTTPD.newFixedLengthResponse(status, mimeType, message);
response.addHeader("Accept-Ranges", "bytes");
return response;
}
@Override
public void stop() {
Logger.d("Simple WebServer stop. port=%s rootdir=%s", "" + getListeningPort(), rootDir);
onStop();
super.stop();
}
protected void onStop() {
}
@Override
public Response serve(IHTTPSession session) {
Map<String, String> headers = session.getHeaders();
String uri = session.getUri();
Method method = session.getMethod();
Map<String, String> params = session.getParms();
logRequest(uri, method, params, headers);
if (!checkClient(uri, headers)) {
return getForbiddenResponse("You are not authorized to access.");
}
try {
uri = modifyUri(uri);
} catch (Exception e) {
return getForbiddenResponse(e.getMessage());
}
Response response = getResponse(uri, params, headers, session);
Logger.d(TAG, "response status=%s", response.getStatus());
return response;
}
protected void logRequest(String uri, Method method, Map<String, String> params, Map<String, String> headers) {
if (uri.contains("/ping/")) {
Logger.v(TAG, "Method:" + method.name() + " Uri: " + uri);
}
else {
Logger.i(TAG, "Method:" + method.name() + " Uri: " + uri);
}
if (!quiet) {
for (Entry<String, String> e : headers.entrySet()) {
Logger.d(TAG, "[Header] %s: %s", e.getKey(), e.getValue());
}
for (Entry<String, String> e : params.entrySet()) {
Logger.d(TAG, "[Param] %s: %s", e.getKey(), e.getValue());
}
}
}
protected Response getResponse(String uri, Map<String, String> param, Map<String, String> headers, IHTTPSession session) {
if (uri.endsWith(".mp4")) {
return respond(Collections.unmodifiableMap(headers), session, uri);
}
else {
return respond(session, uri);
}
}
protected boolean checkClient(String uri, Map<String, String> headers) {
if (!extAccess) {
String httpClientIp = headers.get("http-client-ip");
if (!getHostname().equals(httpClientIp)) {
Logger.e(TAG, "invalid http-client-ip. server=" + getHostname() + " client=" + httpClientIp);
return false;
}
String remoteAddr = headers.get("remote-addr");
if (!getHostname().equals(remoteAddr)) {
Logger.e(TAG, "invalid remote-addr. server=" + getHostname() + " client=" + remoteAddr);
return false;
}
}
if (userAgentList != null) {
String checkedUserAgent = headers.get("user-agent");
if (firstUserAgent == null) {
firstUserAgent = checkedUserAgent;
}
for (String userAgent : userAgentList) { // 動画の場合Chromeが来ないのでチェック無効: ex. Sony401SO Build/23.1.F.0.351 stagefright/1.2 (Linux;Android 5.0.2)
if (checkedUserAgent == null || !isMovie(uri) && (!checkedUserAgent.contains(userAgent) || !firstUserAgent.equals(checkedUserAgent))) {
Logger.e(TAG, "invalid user-agent. required=" + userAgent + " client=" + checkedUserAgent);
return false;
}
}
}
return true;
}
private boolean isMovie(String uri) {
String s = uri.toLowerCase();
return s.endsWith(".mp4") || s.endsWith(".mov") || s.endsWith(".m4v");
}
protected String modifyUri(String uri) throws Exception {
if (uri.endsWith("/")) {
uri = uri + "index.html";
}
return uri;
}
private Response respond(IHTTPSession session, String uri) {
try {
String path = rootDir + uri;
Logger.d(TAG, "Path: %s", path);
String suf = path.contains(".")? path.substring(path.lastIndexOf(".") + 1): "";
String mime = NanoHTTPD.MIME_TYPES.get(suf);
Logger.d(TAG, "mime: %s", mime);
FileInputStream fis = new FileInputStream(path);
return newFixedLengthResponse(Response.Status.OK, mime , fis, fis.available());
} catch (IOException e) {
Logger.e(TAG, "serve failed. " + uri, e);
return newFixedLengthResponse(Response.Status.NOT_FOUND, NanoHTTPD.MIME_PLAINTEXT, "Not Found");
} catch (Exception e) {
Logger.e(TAG, "serve failed. " + uri, e);
return newFixedLengthResponse(Response.Status.INTERNAL_ERROR, NanoHTTPD.MIME_PLAINTEXT, "Internal Server Error");
}
}
private Response respond(Map<String, String> headers, IHTTPSession session, String uri) {
// Remove URL arguments
uri = uri.trim().replace(File.separatorChar, '/');
if (uri.indexOf('?') >= 0) {
uri = uri.substring(0, uri.indexOf('?'));
}
// Prohibit getting out of current directory
if (uri.contains("../")) {
return getForbiddenResponse("Won't serve ../ for security reasons.");
}
// Browsers get confused without '/' after the directory, send a
// redirect.
if (uri.endsWith("/")) {
uri = uri + "index.html";
}
File f = new File(rootDir, uri);
if (!f.exists()) {
return getNotFoundResponse();
}
String mimeTypeForFile = getMimeTypeForFile(uri);
Response response = serveFile(uri, headers, f, mimeTypeForFile);
return response != null ? response : getNotFoundResponse();
}
/**
* Serves file from homeDir and its' subdirectories (only). Uses only URI,
* ignores all headers and HTTP parameters.
*/
Response serveFile(String uri, Map<String, String> header, File file, String mime) {
Response res;
try {
// Calculate etag
String etag = Integer.toHexString((file.getAbsolutePath() + file.lastModified() + "" + file.length()).hashCode());
// Support (simple) skipping:
long startFrom = 0;
long endAt = -1;
String range = header.get("range");
if (range != null) {
if (range.startsWith("bytes=")) {
range = range.substring("bytes=".length());
int minus = range.indexOf('-');
try {
if (minus > 0) {
startFrom = Long.parseLong(range.substring(0, minus));
endAt = Long.parseLong(range.substring(minus + 1));
}
} catch (NumberFormatException ignored) {
}
}
}
// get if-range header. If present, it must match etag or else we
// should ignore the range request
String ifRange = header.get("if-range");
boolean headerIfRangeMissingOrMatching = (ifRange == null || etag.equals(ifRange));
String ifNoneMatch = header.get("if-none-match");
boolean headerIfNoneMatchPresentAndMatching = ifNoneMatch != null && ("*".equals(ifNoneMatch) || ifNoneMatch.equals(etag));
// Change return code and add Content-Range header when skipping is
// requested
long fileLen = file.length();
if (headerIfRangeMissingOrMatching && range != null && startFrom >= 0 && startFrom < fileLen) {
// range request that matches current etag
// and the startFrom of the range is satisfiable
if (headerIfNoneMatchPresentAndMatching) {
// range request that matches current etag
// and the startFrom of the range is satisfiable
// would return range from file
// respond with not-modified
res = newFixedLengthResponse(Response.Status.NOT_MODIFIED, mime, "");
res.addHeader("ETag", etag);
} else {
if (endAt < 0) {
endAt = fileLen - 1;
}
long newLen = endAt - startFrom + 1;
if (newLen < 0) {
newLen = 0;
}
FileInputStream fis = new FileInputStream(file);
fis.skip(startFrom);
res = newFixedLengthResponse(Response.Status.PARTIAL_CONTENT, mime, fis, newLen);
res.addHeader("Accept-Ranges", "bytes");
res.addHeader("Content-Length", "" + newLen);
res.addHeader("Content-Range", "bytes " + startFrom + "-" + endAt + "/" + fileLen);
res.addHeader("ETag", etag);
}
} else {
if (headerIfRangeMissingOrMatching && range != null && startFrom >= fileLen) {
// return the size of the file
// 4xx responses are not trumped by if-none-match
res = newFixedLengthResponse(Response.Status.RANGE_NOT_SATISFIABLE, NanoHTTPD.MIME_PLAINTEXT, "");
res.addHeader("Content-Range", "bytes */" + fileLen);
res.addHeader("ETag", etag);
} else if (range == null && headerIfNoneMatchPresentAndMatching) {
// full-file-fetch request
// would return entire file
// respond with not-modified
res = newFixedLengthResponse(Response.Status.NOT_MODIFIED, mime, "");
res.addHeader("ETag", etag);
} else if (!headerIfRangeMissingOrMatching && headerIfNoneMatchPresentAndMatching) {
// range request that doesn't match current etag
// would return entire (different) file
// respond with not-modified
res = newFixedLengthResponse(Response.Status.NOT_MODIFIED, mime, "");
res.addHeader("ETag", etag);
} else {
// supply the file
res = newFixedFileResponse(file, mime);
res.addHeader("Content-Length", "" + fileLen);
res.addHeader("ETag", etag);
}
}
} catch (IOException ioe) {
res = getForbiddenResponse("Reading file failed.");
}
return res;
}
private Response newFixedFileResponse(File file, String mime) throws FileNotFoundException {
Response res;
res = newFixedLengthResponse(Response.Status.OK, mime, new FileInputStream(file), (int) file.length());
res.addHeader("Accept-Ranges", "bytes");
return res;
}
}
......@@ -56,9 +56,6 @@
</intent-filter>
</receiver>
<service android:name="jp.agentec.abook.abv.cl.beacon.BeaconIntentService" />
<service android:name="jp.agentec.abook.abv.cl.beacon.BeaconPeriodicService" />
<service android:name="jp.agentec.abook.abv.cl.push.ABVFcmListenerService">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT"></action>
......@@ -166,11 +163,6 @@
android:label="LoginActivity"
android:theme="@android:style/Theme.NoTitleBar.Fullscreen" />
<activity
android:name="jp.agentec.abook.abv.ui.home.activity.ContentDetailViewActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
android:label="ContentDetailViewActivity"
android:theme="@style/Theme_Contentdetailview" />
<activity
android:name="jp.agentec.abook.abv.ui.viewer.activity.ContentViewActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
android:hardwareAccelerated="false"
......@@ -206,11 +198,6 @@
android:label="LoginPasswordChangeActivity"
android:theme="@android:style/Theme.NoTitleBar.Fullscreen" >
</activity>
<activity
android:name="jp.agentec.abook.abv.ui.viewer.activity.PanoViewActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
android:theme="@android:style/Theme.NoTitleBar.Fullscreen" >
</activity>
<activity android:name="jp.agentec.abook.abv.ui.home.activity.ABookSettingActivity" android:theme="@android:style/Theme.NoTitleBar"/>
<activity android:name="jp.agentec.abook.abv.ui.home.activity.ChangePasswordActivity" android:theme="@android:style/Theme.NoTitleBar.Fullscreen"/>
<activity android:name="jp.agentec.abook.abv.ui.home.activity.ChangePasswordActivityDialog" android:theme="@style/Theme.MyTheme.ModalDialog"/>
......
......@@ -40,7 +40,6 @@ android {
resValue("bool", "follow_service_option", "${follow_service_option}")
resValue("bool", "is_check_invalid_passward_limit", "${is_check_invalid_passward_limit}")
resValue("bool", "repeat_default", "${repeat_default}")
resValue("integer", "pano_web_server_port", "${pano_web_server_port}")
resValue("string", "version_name", "${version_name}")
resValue("string", "release_date", "${release_date}")
resValue("string", "copy_right", "${copy_right}")
......@@ -53,9 +52,6 @@ android {
resValue("integer", "app_log_retention", "${app_log_retention}")
resValue("bool", "hprof", "${hprof}")
resValue("string", "repository_fqdn", "${repository_fqdn}")
resValue("string", "default_content_key", "${default_content_key}")
resValue("array", "Beacon_UUID", "${Beacon_UUID}")
resValue("integer", "beacon_scan_interval", "${beacon_scan_interval}")
//abvFunctionOptions
resValue("integer", "login_mode", "${login_mode}")
......
......@@ -15,7 +15,6 @@ dependencies {
api 'com.google.firebase:firebase-messaging:11.4.0'
implementation 'commons-codec:commons-codec:1.10'
implementation 'org.xerial:sqlite-jdbc:3.7.2'
implementation 'net.lingala.zip4j:zip4j:1.3.2'
implementation 'com.google.guava:guava:18.0'
implementation 'com.journeyapps:zxing-android-embedded:3.0.1@aar'
......
package jp.agentec.abook.abv.cl.beacon;
public class Beacon {
public String uuid;
public String major;
public String minor;
public int rssi;
public long receiveTime;
public boolean isSameBeacon(Beacon beacon) {
return beacon.uuid.equals(uuid) && beacon.major.equals(major) && beacon.minor.equals(minor);
}
public boolean isNear(){
// 0~5m
return rssi > -70;
}
}
\ No newline at end of file
package jp.agentec.abook.abv.cl.beacon;
import jp.agentec.abook.abv.cl.util.AppUtil;
import jp.agentec.abook.abv.ui.common.appinfo.AppDefType;
import jp.agentec.abook.abv.ui.common.appinfo.AppDefType.BeaconKey;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.support.v4.content.WakefulBroadcastReceiver;
public class BeaconBroadcastReceiver extends WakefulBroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (AppUtil.isAppForground(context)) {
Intent i = new Intent(AppDefType.Intent.Action_Receive_Message);
i.putExtra(BeaconKey.major, intent.getStringExtra(BeaconKey.major));
i.putExtra(BeaconKey.minor, intent.getStringExtra(BeaconKey.minor));
context.sendBroadcast(i);
} else {
ComponentName comp = new ComponentName(context.getPackageName(), BeaconIntentService.class.getName());
startWakefulService(context, (intent.setComponent(comp)));
}
}
}
package jp.agentec.abook.abv.cl.beacon;
import jp.agentec.abook.abv.launcher.android.R;
import jp.agentec.abook.abv.ui.common.activity.ABVActivity;
import jp.agentec.abook.abv.ui.common.appinfo.AppDefType.BeaconKey;
import android.annotation.TargetApi;
import android.app.IntentService;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Build.VERSION_CODES;
import android.os.Bundle;
import android.support.v4.app.NotificationCompat;
import android.support.v4.content.WakefulBroadcastReceiver;
public class BeaconIntentService extends IntentService {
private NotificationManager mNotificationManager;
NotificationCompat.Builder builder;
private static int mNotificationConnect = 0;
public BeaconIntentService() {
super("BeaconIntentService");
}
@Override
protected void onHandleIntent(Intent intent) {
Bundle extras = intent.getExtras();
if (!extras.isEmpty()) {
sendNotification(extras.getString(BeaconKey.major), extras.getString(BeaconKey.minor));
}
WakefulBroadcastReceiver.completeWakefulIntent(intent);
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
private void sendNotification(String major, String minor) {
mNotificationManager = (NotificationManager) this.getSystemService(Context.NOTIFICATION_SERVICE);
Notification.Builder nb = new Notification.Builder(this);
nb.setDefaults(Notification.DEFAULT_ALL);
nb.setSmallIcon(R.drawable.icon);
nb.setWhen(System.currentTimeMillis());
nb.setAutoCancel(true);
Intent intent = new Intent(Intent.ACTION_VIEW);
String downloadUrl = getDownloadUrl(major, minor);
Uri.Builder uriBuilder = new Uri.Builder();
uriBuilder.scheme(getString(R.string.scheme_url));
uriBuilder.authority("abook");
uriBuilder.appendQueryParameter(ABVActivity.URL, downloadUrl);
uriBuilder.appendQueryParameter(ABVActivity.PASSWORD, "" + 0);
intent.setData(uriBuilder.build());
PendingIntent pi = PendingIntent.getActivity(this, 0, intent, 0);
nb.setContentTitle(getString(R.string.app_name));
nb.setContentText(getString(R.string.receive_message)+ "(" + major + minor + ")");
nb.setContentIntent(pi);
Notification n;
if (android.os.Build.VERSION.SDK_INT < VERSION_CODES.JELLY_BEAN_MR1) {
//noinspection deprecation(API16から非推奨になった無視)
n = nb.getNotification();
} else {
n = nb.build();
}
n.flags |= Notification.FLAG_SHOW_LIGHTS;
n.flags |= Notification.FLAG_AUTO_CANCEL;
// Notification ID 증가 0~999 까지 순환
mNotificationConnect = (mNotificationConnect + 1) % 1000;
mNotificationManager.notify(mNotificationConnect, n);
}
// テキスト内からダウンロードURLを検索して返します
private String getDownloadUrl(String major, String minor) {
return "http://" + getString(R.string.repository_fqdn) + "/" + major + minor;
}
}
package jp.agentec.abook.abv.cl.beacon;
import java.util.ArrayList;
import java.util.List;
import jp.agentec.abook.abv.bl.common.CommonExecutor;
import jp.agentec.abook.abv.bl.common.log.Logger;
import jp.agentec.abook.abv.bl.data.dao.AbstractDao;
import jp.agentec.abook.abv.bl.data.dao.BeaconHistoryDao;
import jp.agentec.abook.abv.bl.dto.BeaconHistoryDto;
import jp.agentec.abook.abv.launcher.android.R;
import jp.agentec.abook.abv.ui.common.appinfo.AppDefType;
import jp.agentec.abook.abv.ui.home.activity.SplashScreenActivity;
import android.annotation.TargetApi;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.app.Service;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.os.Binder;
import android.os.Build;
import android.os.IBinder;
import android.os.Parcel;
import android.os.RemoteException;
public class BeaconPeriodicService extends Service {
private final String TAG = "BeaconPeriodicService";
private List<Beacon> beaconList;
private List<Beacon> sentBeaconList = new ArrayList<>();
private BluetoothAdapter mBluetoothAdapter;
private BluetoothAdapter.LeScanCallback mLeScanCallback;
public static BeaconPeriodicService activeService;
private int containInterval = 1000 * 20;
protected void execTask() {
activeService = this;
iBeaconScan();
}
/**
* 次回の実行計画を立てる。
*/
protected void makeNextPlan() {
this.scheduleNextTime();
}
protected final IBinder binder = new Binder() {
@Override
protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
return super.onTransact(code, data, reply, flags);
}
};
@Override
public IBinder onBind(Intent intent) {
return binder;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
init();
execTask();
return START_STICKY;
}
/**
* サービスの次回の起動を予約
*/
public void scheduleNextTime() {
long now = System.currentTimeMillis();
PendingIntent alarmSender = PendingIntent.getService(this, 0, new Intent(this, this.getClass()), 0);
AlarmManager am = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE);
am.set(AlarmManager.RTC, now + getResources().getInteger(R.integer.beacon_scan_interval), alarmSender);
}
/**
* サービスの定期実行を解除し,サービスを停止
*/
public void stopResident(Context context) {
Intent intent = new Intent(context, this.getClass());
PendingIntent pendingIntent = PendingIntent.getService(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
alarmManager.cancel(pendingIntent);
stopSelf();
}
@Override
public void onDestroy() {
super.onDestroy();
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
private synchronized void iBeaconScan() {
final BluetoothManager bluetoothManager = (BluetoothManager) getApplicationContext().getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = bluetoothManager.getAdapter();
if (mBluetoothAdapter != null && mBluetoothAdapter.isEnabled()) {
CommonExecutor.execute(new Runnable() {
@Override
public void run() {
leScan();
}
});
} else {
makeNextPlan();
}
}
private String IntToHex(int Value) {
char HEX2[] = { Character.forDigit((Value >> 4) & 0x0F, 16), Character.forDigit(Value & 0x0F, 16) };
return new String(HEX2);
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
private void init(){
mLeScanCallback = new BluetoothAdapter.LeScanCallback() {
@Override
public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) {
if (scanRecord.length > 30) {
if ((scanRecord[5] == (byte) 0x4c) && (scanRecord[6] == (byte) 0x00) && (scanRecord[7] == (byte) 0x02) && (scanRecord[8] == (byte) 0x15)) {
String uuid = IntToHex(scanRecord[9] & 0xff)
+ IntToHex(scanRecord[10] & 0xff)
+ IntToHex(scanRecord[11] & 0xff)
+ IntToHex(scanRecord[12] & 0xff)
+ "-"
+ IntToHex(scanRecord[13] & 0xff)
+ IntToHex(scanRecord[14] & 0xff)
+ "-"
+ IntToHex(scanRecord[15] & 0xff)
+ IntToHex(scanRecord[16] & 0xff)
+ "-"
+ IntToHex(scanRecord[17] & 0xff)
+ IntToHex(scanRecord[18] & 0xff)
+ "-"
+ IntToHex(scanRecord[19] & 0xff)
+ IntToHex(scanRecord[20] & 0xff)
+ IntToHex(scanRecord[21] & 0xff)
+ IntToHex(scanRecord[22] & 0xff)
+ IntToHex(scanRecord[23] & 0xff)
+ IntToHex(scanRecord[24] & 0xff);
if(!isAvailableServiceUUID(uuid)){
return;
}
String major = IntToHex(scanRecord[25] & 0xff) + IntToHex(scanRecord[26] & 0xff);
String minor = IntToHex(scanRecord[27] & 0xff) + IntToHex(scanRecord[28] & 0xff);
Beacon beacon = new Beacon();
beacon.uuid = uuid;
beacon.major = major;
beacon.minor = minor;
beacon.rssi = rssi;
beacon.receiveTime = System.currentTimeMillis();
Logger.d(TAG, "Beacon received UUID:" + beacon.uuid + ", Major:" + beacon.major + ", Minor: " + beacon.minor + ", Rssi: " + beacon.rssi);
if(!isBeaconListContains(beaconList, beacon) && beacon.isNear()){
beaconList.add(beacon);
}
}
}
}
};
}
private boolean isBeaconListContains(List<Beacon> beaconList, Beacon beacon){
for (Beacon b : beaconList) {
if (beacon.isSameBeacon(b)) {
return true;
}
}
return false;
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
private synchronized void leScan() {
beaconList = new ArrayList<>();
// TODO later UUIDのフィルタリングがうまく動作しない。
Logger.d(TAG, "startLeScan");
//noinspection deprecation(API21から非推奨になった、無視)
mBluetoothAdapter.startLeScan(mLeScanCallback);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
//ignore
}
Logger.d(TAG, "stoptLeScan");
//noinspection deprecation(API21から非推奨になった、無視)
mBluetoothAdapter.stopLeScan(mLeScanCallback);
sendBeaconBroadcast();
makeNextPlan();
}
private void sendBeaconBroadcast() {
if(isSplashScreenForground(this)){
return;
}
Beacon targetBeacon = getNearestBeacon();
if (targetBeacon != null) {
if (isUnSentBeacon(targetBeacon)) {
sendBroadcastBeaconMessage(targetBeacon);
saveBeaconHistory(targetBeacon);
}
}
resetSentBeaconList();
}
private Beacon getNearestBeacon() {
Beacon targetBeacon = null;
for (Beacon beacon : beaconList) {
if (targetBeacon == null) {
targetBeacon = beacon;
} else {
if (targetBeacon.rssi < beacon.rssi) {
targetBeacon = beacon;
}
}
}
return targetBeacon;
}
private boolean isUnSentBeacon(Beacon beacon){
return !isBeaconListContains(sentBeaconList, beacon);
}
private void sendBroadcastBeaconMessage(Beacon beacon) {
Logger.d(TAG, "SendBroadcastBeaconMessage UUID:" + beacon.uuid + ", Major:" + beacon.major + ", Minor: " + beacon.minor + ", Rssi: " + beacon.rssi);
Intent it = new Intent(BeaconPeriodicService.this, BeaconBroadcastReceiver.class);
it.setAction(AppDefType.Intent.Action_Receive_iBeacon_Message);
it.putExtra("major", beacon.major.toUpperCase());
it.putExtra("minor", beacon.minor.toUpperCase());
sendBroadcast(it);
// 送信済みリストに保存
sentBeaconList.add(beacon);
}
private void saveBeaconHistory(Beacon beacon) {
BeaconHistoryDto dto = new BeaconHistoryDto();
dto.major = Integer.parseInt(beacon.major, 16);
dto.minor = Integer.parseInt(beacon.minor, 16);
AbstractDao.getDao(BeaconHistoryDao.class).insertOrUpdate(dto);
}
private void resetSentBeaconList(){
for (int i = sentBeaconList.size() - 1; i >= 0; i--) {
Beacon sentBeacon = sentBeaconList.get(i);
boolean isRemoveTarget = true;
for (Beacon beacon : beaconList) {
if (sentBeacon.isSameBeacon(beacon)) {
sentBeacon.receiveTime = beacon.receiveTime;
isRemoveTarget = false;
break;
}
}
if (isRemoveTarget) {
if (sentBeacon.receiveTime + containInterval < System.currentTimeMillis()) {
Logger.d(TAG, "Remove UUID:" + sentBeacon.uuid + ", Major:" + sentBeacon.major + ", Minor: " + sentBeacon.minor + ", Rssi: " + sentBeacon.rssi);
sentBeaconList.remove(i);
}
}
}
}
private boolean isAvailableServiceUUID(String serviceUUID) {
String[] uuids = getResources().getStringArray(R.array.Beacon_UUID);
for (String uuid : uuids) {
if (serviceUUID.toUpperCase().equals(uuid.toUpperCase())) {
return true;
}
}
return false;
}
private boolean isSplashScreenForground(Context context) {
ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
//noinspection deprecation(API21から非推奨になった。無視)
List<RunningTaskInfo> taskInfoList = am.getRunningTasks(1);
if (taskInfoList.size() > 0) {
RunningTaskInfo runningTaskInfo = taskInfoList.get(0);
ComponentName topActivity = runningTaskInfo.topActivity;
if (topActivity.getPackageName().equals(context.getPackageName()) && topActivity.getClassName().equals(SplashScreenActivity.class.getName())) {
return true;
}
}
return false;
}
}
\ No newline at end of file
......@@ -61,8 +61,6 @@ public class ABVUIDataCache {
private int homeContractId;
private int homeSiteId;
public int contentShareType = 0; // 0:なし,1:ABook+,2:Reader
public boolean preIsCatalogEdition;
public int preScale;
public int preDeviceLocationMode;
......@@ -106,7 +104,6 @@ public class ABVUIDataCache {
loadHomeContractId();
loadHomeSiteId();
loadContentLocationTypeEnable();
contentShareType = AbstractLogic.getLogic(ContractLogic.class).getContentShareType();
}
public boolean getFavoriteFlag() {
......
package jp.agentec.abook.abv.launcher.android;
import java.util.Calendar;
import jp.agentec.abook.abv.bl.common.ABVEnvironment;
import jp.agentec.abook.abv.bl.common.log.Logger;
import jp.agentec.abook.abv.cl.beacon.BeaconPeriodicService;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
......
......@@ -85,8 +85,6 @@ import jp.agentec.abook.abv.bl.logic.ContractLogic;
import jp.agentec.abook.abv.bl.logic.GroupLogic;
import jp.agentec.abook.abv.bl.logic.MemoLogic;
import jp.agentec.abook.abv.bl.logic.UserAuthenticateLogic;
import jp.agentec.abook.abv.bl.net.PanoServer;
import jp.agentec.abook.abv.bl.repo.RepoClient;
import jp.agentec.abook.abv.cl.billing.Purchase;
import jp.agentec.abook.abv.cl.helper.ABVUncaughtExceptionHandler;
import jp.agentec.abook.abv.cl.util.AndroidStringUtil;
......@@ -279,27 +277,6 @@ public abstract class ABVAuthenticatedActivity extends ABVActivity implements Co
}
}
public void contentDetailActivityMove(long contentId, String className) {
contentDetailActivityMove(contentId, className, -1, false);
}
// 2012 12 17 Contents link by jeonghun
// ActivityView에서 사용할수 있기 위하여 contentDetailActivityMove 추가
// 상세화면에서 이전화면 이동시 Bug수정
protected void contentDetailActivityMove(final long contentId, final String className, final int pre_page, final boolean contentLinkFlag) {
showProgressPopup();
ActivityHandlingHelper.getInstance().contentDetailActivityMove(this, contentId, className, pre_page, contentLinkFlag, new Callback() {
@Override
public Object callback(Object ret) {
closeProgressPopup();
if (ret != null) {
handleErrorMessageToast(ret);
}
return null;
}
});
}
/**
* Viewを開始します。
*/
......@@ -1065,7 +1042,7 @@ public abstract class ABVAuthenticatedActivity extends ABVActivity implements Co
}
if (xWalkView != null) {
// xWalkViewがnullではない場合、javaScriptでopengetitsshareコマンド実行
javaScriptSignageCmd(PanoServer.CMD_GETITS_OPEN_SHARE, xWalkView);
javaScriptSignageCmd(ABookKeys.CMD_GETITS_OPEN_SHARE, xWalkView);
}
}
});
......
package jp.agentec.abook.abv.ui.common.activity;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import jp.agentec.abook.abv.bl.acms.client.json.content.ContentJSON;
import jp.agentec.abook.abv.bl.acms.type.DownloadStatusType;
import jp.agentec.abook.abv.bl.common.ABVEnvironment;
import jp.agentec.abook.abv.bl.common.Callback;
import jp.agentec.abook.abv.bl.common.CommonExecutor;
import jp.agentec.abook.abv.bl.common.Constant.ReaderConstant;
import jp.agentec.abook.abv.bl.common.Constant.ReaderConstant.ReceivePushMessage;
import jp.agentec.abook.abv.bl.common.exception.ABVException;
import jp.agentec.abook.abv.bl.common.exception.ABVExceptionCode;
import jp.agentec.abook.abv.bl.common.exception.AcmsException;
import jp.agentec.abook.abv.bl.common.exception.NetworkDisconnectedException;
import jp.agentec.abook.abv.bl.common.log.Logger;
import jp.agentec.abook.abv.bl.common.util.SecurityUtil;
import jp.agentec.abook.abv.bl.data.ABVDataCache;
import jp.agentec.abook.abv.bl.data.dao.AbstractDao;
import jp.agentec.abook.abv.bl.data.dao.ContentIdConvDao;
import jp.agentec.abook.abv.bl.data.dao.ContractDao;
import jp.agentec.abook.abv.bl.data.dao.LockDao;
import jp.agentec.abook.abv.bl.data.dao.SiteDao;
import jp.agentec.abook.abv.bl.download.ContentDownloader;
import jp.agentec.abook.abv.bl.download.ContentFileExtractor;
import jp.agentec.abook.abv.bl.download.ContentZipDownloadNotification;
import jp.agentec.abook.abv.bl.download.ReaderContentDownloader;
import jp.agentec.abook.abv.bl.dto.ContentDto;
import jp.agentec.abook.abv.bl.dto.ContractDto;
import jp.agentec.abook.abv.bl.dto.SiteDto;
import jp.agentec.abook.abv.bl.logic.AbstractLogic;
import jp.agentec.abook.abv.bl.logic.ContentLogic;
import jp.agentec.abook.abv.bl.logic.ContractLogic;
import jp.agentec.abook.abv.bl.repo.RepoClient;
import jp.agentec.abook.abv.cl.helper.ContentMarkingFileHelper;
import jp.agentec.abook.abv.cl.push.FcmManager;
import jp.agentec.abook.abv.cl.util.AndroidStringUtil;
import jp.agentec.abook.abv.cl.util.PreferenceUtil;
import jp.agentec.abook.abv.cl.util.StorageUtil;
import jp.agentec.abook.abv.launcher.android.ABVApplication;
import jp.agentec.abook.abv.launcher.android.R;
import jp.agentec.abook.abv.ui.common.appinfo.AppDefType.SubMenuType;
import jp.agentec.abook.abv.ui.common.appinfo.AppDefType.UserPrefKey;
import jp.agentec.abook.abv.ui.common.appinfo.options.Options;
import jp.agentec.abook.abv.ui.common.constant.ErrorCode;
import jp.agentec.abook.abv.ui.common.constant.ErrorMessage;
import jp.agentec.abook.abv.ui.common.constant.NaviConsts;
import jp.agentec.abook.abv.ui.common.dialog.ABookAlertDialog;
import jp.agentec.abook.abv.ui.common.helper.NavigationContentDownloadForReader;
import jp.agentec.abook.abv.ui.common.helper.NavigationContentDownloadForReader.NavigationForReaderInterface;
import jp.agentec.abook.abv.ui.common.helper.NavigationDownloadForReader;
import jp.agentec.abook.abv.ui.common.helper.NavigationExceptionForReader;
import jp.agentec.abook.abv.ui.common.helper.NavigationOpenForReader;
import jp.agentec.abook.abv.ui.common.helper.NavigationRequestPassForReader;
import jp.agentec.abook.abv.ui.common.helper.NavigationUpdateForReader;
import jp.agentec.abook.abv.ui.common.helper.ProgressDialogHelper;
import jp.agentec.abook.abv.ui.common.util.ABVToastUtil;
import jp.agentec.abook.abv.ui.common.util.AlertDialogUtil;
import jp.agentec.abook.abv.ui.common.util.LogUtil;
import jp.agentec.abook.abv.ui.common.vo.ContentMyList;
import jp.agentec.abook.abv.ui.home.activity.ABookSettingActivity;
import jp.agentec.abook.abv.ui.home.adapter.ContentCheckListTypeItem;
import jp.agentec.abook.abv.ui.home.helper.ActivityHandlingHelper;
import jp.agentec.adf.util.StringUtil;
import android.content.DialogInterface;
import android.content.Intent;
......@@ -514,275 +490,6 @@ public abstract class ABVUIActivity extends ABVAuthenticatedActivity {
}
}
@Deprecated
// Widgetは廃止
protected void sendBroadcastWidget(String action) {
Intent intent = new Intent();
intent.setAction(action);
getApplicationContext().sendBroadcast(intent);
}
/**
* サブメニューで表示するアイテムリストを作成します。
*
* @param contentDto 対象コンテンツ
* @param isMain true:メイン画面用、false:詳細画面用
* @return
*/
public List<ContentCheckListTypeItem> createSubMenuItems(ContentDto contentDto, boolean isMain) {
String[] names = getRStringArray(R.array.content_submenu_names);
List<ContentCheckListTypeItem> items = new ArrayList<>();
if (isMain) {
// コンテンツ詳細 ※詳細画面ではなし
items.add(new ContentCheckListTypeItem(SubMenuType.CONTENT_DETAIL_OPEN, names[SubMenuType.CONTENT_DETAIL_OPEN]));
}
if (contentDto.downloadingFlg) { // ダウンロード中コンテンツ
// ダウンロードキャンセル
items.add(new ContentCheckListTypeItem(SubMenuType.CONTENT_DOWNLOAD_CANCEL, names[SubMenuType.CONTENT_DOWNLOAD_CANCEL]));
} else {
if (isMain && isStreamingEnable(contentDto)) {
items.add(new ContentCheckListTypeItem(SubMenuType.CONTENT_STREAMING, names[SubMenuType.CONTENT_STREAMING]));
}
if (contentDto.downloadedFlg) { // ダウンロード済みコンテンツ
if (isMain) {
// コンテンツ閲覧 ※詳細画面ではなし
items.add(new ContentCheckListTypeItem(SubMenuType.CONTENT_OPEN, names[SubMenuType.CONTENT_OPEN]));
if (ActivityHandlingHelper.getInstance().isUpdateEnable(contentDto) && !ABVDataCache.getInstance().serviceOption.isPayment()) {
// アップデート ※詳細画面ではなし
items.add(new ContentCheckListTypeItem(SubMenuType.CONTENT_UPDATE, names[SubMenuType.CONTENT_UPDATE]));
}
}
// コンテンツ削除
//チェックビルドオプション
if (getRInteger(R.integer.hide_delete_content) == 0) {
items.add(new ContentCheckListTypeItem(SubMenuType.CONTENT_DELETE, names[SubMenuType.CONTENT_DELETE]));
}
if (contentDto.contentType == null || ContentJSON.KEY_PDF_TYPE.equals(contentDto.contentType.toLowerCase(Locale.US)) || ContentJSON.KEY_NONE_TYPE.equals(contentDto.contentType)) {
// メモ、マーキング、しおりがすべてオフの場合非表示
if (Options.getInstance(this).getViewerMenuMemo() == 1 || Options.getInstance(this).getViewerMenuMarking() == 1 || Options.getInstance(this).getViewerMenuBookmark() == 1) {
// ユーザ情報コピー
items.add(new ContentCheckListTypeItem(SubMenuType.USER_INFO_COPY, names[SubMenuType.USER_INFO_COPY]));
}
if (getABVUIDataCache().srcContentId != -1) {
// ユーザ情報貼り付け
items.add(new ContentCheckListTypeItem(SubMenuType.USER_INFO_PASTE, names[SubMenuType.USER_INFO_PASTE]));
}
}
if (Options.getInstance(this).getHomeMenuMyfolder() == 1 && isMain) {
// マイフォルダに追加 ※詳細画面ではなし
items.add(new ContentCheckListTypeItem(SubMenuType.MYFOLDER_ADD, names[SubMenuType.MYFOLDER_ADD]));
}
} else { // 未ダウンロードコンテンツ
if (ActivityHandlingHelper.getInstance().isDownloadEnable(contentDto) && !ABVDataCache.getInstance().serviceOption.isPayment()) {
// ダウンロード
items.add(new ContentCheckListTypeItem(SubMenuType.CONTENT_DOWNLOAD, names[SubMenuType.CONTENT_DOWNLOAD]));
}
}
}
if (isMain && Options.getInstance(this).getFilerFavorite() == 1) {
if (contentDto.favoriteFlg) {
// お気に入り追加 ※詳細画面ではなし
items.add(new ContentCheckListTypeItem(SubMenuType.FAVORITE_DELETE, names[SubMenuType.FAVORITE_DELETE]));
} else {
// お気に入り削除 ※詳細画面ではなし
items.add(new ContentCheckListTypeItem(SubMenuType.FAVORITE_ADD, names[SubMenuType.FAVORITE_ADD]));
}
}
// サービスオプションチェック
if (Options.getInstance(this).getViewerMenuShare(contentDto.contentId) == 1 && (ABVDataCache.getInstance().serviceOption.isBizEdition() || hasShareUrl(contentDto))) {
items.add(new ContentCheckListTypeItem(SubMenuType.SHARE_CONTENT, names[SubMenuType.SHARE_CONTENT]));
}
return items;
}
/**
* URL解決を伴わないReaderコンテンツダウンロード
*
* @param contentId
* @return
*/
protected boolean readerContentDownload(final ContractDto contractDto, final long contentId){
final StringBuffer result = new StringBuffer();
try {
showWifiDisconnectAlert(R.string.C_E_SYSTEM_0005, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
String msg = null;
SiteDto siteDto = AbstractDao.getDao(SiteDao.class).getDto(contractDto.siteId);
try {
// マスタ転送
if (transferMaster(getRString(R.string.repository_fqdn)) != null) { //????? !=null
throw new Exception();
}
ReaderContentDownloader.getInstance().downloadContent(contractDto.siteId, ContentLogic.getCmsContentId(contentId), siteDto.downloadUrl.replace("{URLPATH}", contractDto.urlPath), contractDto.contractId, -1, null);
} catch (AcmsException e) {// TODO: LoginErrorMessageにメッセージが入っているがこれで良いか。CMS側と合わせて修正必要。
Logger.e(TAG, "downloadContent failed. " + e.getMessage() + " : " + e.getLoginErrorMessage());
msg = handleException(e);
} catch (NetworkDisconnectedException e) {
Logger.e(TAG, "downloadContent failed. " + e.toString());
msg = ErrorMessage.getErrorMessage(ABVUIActivity.this, ErrorCode.NETWORK);
} catch (Exception e) {
Logger.e(TAG, "downloadContent failed. ", e);
msg = ErrorMessage.getErrorMessage(ABVUIActivity.this, ErrorCode.C_E_SYSTEM_0001);
} finally {
if (!StringUtil.isNullOrEmpty(msg)) {
result.append(msg);
final String errorMsg = msg;
handler.post(new Runnable() {
@Override
public void run() {
showSimpleAlertDialog(getRString(R.string.error), errorMsg);
}
});
}
}
}
}, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
ContentZipDownloadNotification notification = new ContentZipDownloadNotification();
notification.contentId = contentId;
notification.downloadStatus = DownloadStatusType.Canceled;
onDownloadingContentZip(notification);
}
});
} catch (NetworkDisconnectedException e) {
result.append(e.toString());
handleErrorMessageToast(ErrorCode.NETWORK);
}
return result.length() == 0;
}
protected ArrayList<Long> readerContentDownload(ContractDto contractDto, long[] contentIds) {
String msg;
ArrayList<Long> successIds = new ArrayList<>();
SiteDto siteDto = AbstractDao.getDao(SiteDao.class).getDto(contractDto.siteId);
// マスタ転送
try {
msg = transferMaster(getRString(R.string.repository_fqdn));
} catch (AcmsException e) {
Logger.e(TAG, "downloadContent failed. " + e.getMessage() + " : " + e.getLoginErrorMessage());
msg = handleException(e);
} catch (NetworkDisconnectedException e) {
Logger.e(TAG, "downloadContent failed. " + e.toString());
msg = ErrorMessage.getErrorMessage(this, ErrorCode.NETWORK);
}
if (msg == null) {
for (long contentId : contentIds) {
try {
ReaderContentDownloader.getInstance().downloadContent(contractDto.siteId, ContentLogic.getCmsContentId(contentId), siteDto.downloadUrl.replace("{URLPATH}", contractDto.urlPath), contractDto.contractId, -1, null);
successIds.add(contentId);
} catch (Exception e) {
msg = ErrorMessage.getErrorMessage(this, ErrorCode.M003);
Logger.e(TAG, "downloadContent failed. " + e.toString());
}
}
}
if (!StringUtil.isNullOrEmpty(msg)) {
final String errorMsg = msg;
handler.post(new Runnable() {
@Override
public void run() {
ABVToastUtil.showMakeText(ABVUIActivity.this, errorMsg, Toast.LENGTH_LONG);
}
});
}
return successIds;
}
/**
* マスタ転送を行い、prefに実行日時を保存する
*
* @return
* @throws AcmsException
* @throws NetworkDisconnectedException
*/
protected String transferMaster(final String repositoryFqdn) throws AcmsException, NetworkDisconnectedException {
if (isCheckTransferMaster) {
return null;
}
try {
isCheckTransferMaster = true;
return RepoClient.getInstance().transferMaster(repositoryFqdn);
}
finally {
PreferenceUtil.putUserPref(this, UserPrefKey.LAST_MASTER_TRANSFER_TIME, System.currentTimeMillis());
isCheckTransferMaster = false;
}
}
private void pushMessageActivityFinish() {
if (this instanceof ShowPushMessageDailogActivity) {
finish();
}
}
private String createMessageWhenPushMessageExist(String msg) {
if (!StringUtil.isNullOrEmpty(mPushMessage)) {
msg = mPushMessage + "\n" + msg;
mPushMessage = "";
}
return msg;
}
private void showPushMessageDialog(final ContractDto contractDto, final Callback callback) {
ABookAlertDialog pushDialog = AlertDialogUtil.createAlertDialog(this, getRString(R.string.push_message_setting), getRString(R.string.push_message_setting_dialog_summary));
pushDialog.setPositiveButton(R.string.agree, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
contractDto.receivePushMessage = ReceivePushMessage.PERMIT;
callback.callback(contractDto);
}
});
pushDialog.setNegativeButton(R.string.disagree, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
contractDto.receivePushMessage = ReceivePushMessage.REFUSE;
callback.callback(contractDto);
}
});
showAlertDialog(pushDialog);
}
private void updateReceivePushMessage(final ContractDto contractDto) {
ProgressDialogHelper.showProgressPopup(this, true);
final String fcmToken = FcmManager.getFcmToken(this);
if (fcmToken == null || fcmToken.equals("noneId")) {
ABVToastUtil.showMakeText(ABVUIActivity.this, getString(R.string.fcm_not_supported), Toast.LENGTH_SHORT);
} else {
CommonExecutor.execute(new Runnable() {
@Override
public void run() {
try {
AbstractLogic.getLogic(ContractLogic.class).updateReceivePushMessage(ABVUIActivity.this.getString(R.string.repository_fqdn), fcmToken, contractDto);
} catch (AcmsException e) {
ErrorMessage.showErrorMessageToast(ABVUIActivity.this, e);
Logger.e("PushMessageSettingDialog", e);
} catch (NetworkDisconnectedException e) {
ABVToastUtil.showMakeText(ABVUIActivity.this, R.string.NETWORK, Toast.LENGTH_SHORT);
} finally {
ProgressDialogHelper.closeProgressPopup();
}
}
});
}
}
@Override
public void onDownloadingContentZip(final ContentZipDownloadNotification notification) {
super.onDownloadingContentZip(notification);
......
......@@ -78,7 +78,6 @@ import jp.agentec.abook.abv.ui.common.util.ABVToastUtil;
import jp.agentec.abook.abv.ui.common.util.AlertDialogUtil;
import jp.agentec.abook.abv.ui.common.util.ClipboardUtil;
import jp.agentec.abook.abv.ui.home.dialog.BackupDialog;
import jp.agentec.abook.abv.ui.home.dialog.PushMessageSettingDialog;
import jp.agentec.abook.abv.ui.home.dialog.RestoreDialog;
import jp.agentec.abook.abv.ui.home.helper.ABookPermissionHelper;
import jp.agentec.abook.abv.ui.home.helper.ActivityHandlingHelper;
......@@ -295,12 +294,6 @@ public class ABookSettingFragment extends PreferenceFragment {
getPreferenceScreen().removePreference(prefGroup);
}
private void showPushMessageSettingDialog() {
PushMessageSettingDialog dialog = new PushMessageSettingDialog(getActivity());
dialog.setNormalSizeFlag(isNormalSize());
dialog.show();
}
private void setViewerSetting() {
PreferenceGroup viewerSettings = (PreferenceGroup) findPreference(VIEWER_SETTING);
......
package jp.agentec.abook.abv.ui.home.activity;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import jp.agentec.abook.abv.bl.acms.client.json.DownloadedContentInfoJSON;
import jp.agentec.abook.abv.bl.acms.client.json.content.ContentJSON;
import jp.agentec.abook.abv.bl.acms.client.json.content.ContentLinkJSON;
import jp.agentec.abook.abv.bl.acms.type.DeliveryType;
import jp.agentec.abook.abv.bl.acms.type.ServiceOption.ContentShareType;
import jp.agentec.abook.abv.bl.common.ABVEnvironment;
import jp.agentec.abook.abv.bl.common.exception.AcmsException;
import jp.agentec.abook.abv.bl.common.exception.NetworkDisconnectedException;
import jp.agentec.abook.abv.bl.common.log.Logger;
import jp.agentec.abook.abv.bl.common.util.ContentFileUtil;
import jp.agentec.abook.abv.bl.data.ABVDataCache;
import jp.agentec.abook.abv.bl.data.dao.AbstractDao;
import jp.agentec.abook.abv.bl.data.dao.ContentDao;
import jp.agentec.abook.abv.bl.data.dao.ContentIdConvDao;
import jp.agentec.abook.abv.bl.download.ContentZipDownloadNotification;
import jp.agentec.abook.abv.bl.dto.ContentDto;
import jp.agentec.abook.abv.bl.dto.ContentIdConvDto;
import jp.agentec.abook.abv.bl.dto.ContractDto;
import jp.agentec.abook.abv.bl.logic.AbstractLogic;
import jp.agentec.abook.abv.bl.logic.ContentLogic;
import jp.agentec.abook.abv.bl.logic.ContractLogic;
import jp.agentec.abook.abv.bl.logic.ProjectLogic;
import jp.agentec.abook.abv.cl.util.AndroidStringUtil;
import jp.agentec.abook.abv.cl.util.BitmapUtil;
import jp.agentec.abook.abv.cl.util.PreferenceUtil;
import jp.agentec.abook.abv.launcher.android.R;
import jp.agentec.abook.abv.launcher.android.R.id;
import jp.agentec.abook.abv.ui.common.activity.ABVUIActivity;
import jp.agentec.abook.abv.ui.common.appinfo.AppDefType.DefPrefKey;
import jp.agentec.abook.abv.ui.common.appinfo.AppDefType.SubMenuType;
import jp.agentec.abook.abv.ui.common.appinfo.options.Options;
import jp.agentec.abook.abv.ui.common.constant.ErrorCode;
import jp.agentec.abook.abv.ui.common.constant.ErrorMessage;
import jp.agentec.abook.abv.ui.common.dialog.ABookAlertDialog;
import jp.agentec.abook.abv.ui.common.dto.ContentPageSerializableDto;
import jp.agentec.abook.abv.ui.common.helper.BillingHelper;
import jp.agentec.abook.abv.ui.common.util.ABVToastUtil;
import jp.agentec.abook.abv.ui.common.util.AlertDialogUtil;
import jp.agentec.abook.abv.ui.common.view.ABVPopupListWindow;
import jp.agentec.abook.abv.ui.home.adapter.ContentCheckListTypeItem;
import jp.agentec.abook.abv.ui.home.adapter.ContentDetailLinkAdapter;
import jp.agentec.abook.abv.ui.home.adapter.ContentDetailListAdapter;
import jp.agentec.abook.abv.ui.home.dto.ContentLinkAdapterDto;
import jp.agentec.abook.abv.ui.home.dto.ContentLinkAdapterDto.ContentStatus;
import jp.agentec.abook.abv.ui.home.helper.ActivityHandlingHelper;
import jp.agentec.adf.util.DateTimeUtil;
import jp.agentec.adf.util.StringUtil;
import org.json.adf.JSONArray;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.Color;
import android.os.Bundle;
import android.util.Log;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.WindowManager;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.Button;
import android.widget.GridView;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TabHost;
import android.widget.TabHost.OnTabChangeListener;
import android.widget.TabHost.TabSpec;
import android.widget.TextView;
import android.widget.Toast;
/**
* コンテンツ詳細画面
* @author Chae
* @version 1.0.0
*/
public class ContentDetailViewActivity extends ABVUIActivity {
private static final String TAG = "ContentDetailActivity";
private ContractLogic contractLogic = AbstractLogic.getLogic(ContractLogic.class);
private TextView mTxtContentDetail;
private TextView mTxtDeliveryDateStart;
private TextView mTxtDeliveryDateEnd;
private TextView mTxtPublisherInfo;
private TextView mTxtPublisherId;
private TextView mTxtLastDeliveryDateValue;
private TextView mTxtContentSizeValue;
private TextView mTxtContractContentId;
private TextView mTxtAllPageNum;
private TextView mTxtGroupInfo;
private TextView mTxtCategoryInfo;
private TabHost mTabHost;
private Button mBtnDelete;
private Button mBtnDownloadStatus;
private Button mBtnOpenStatus;
private Button mBtnStreaming;
private Button mBtnInappPurchase;
private Button mBtnSubscribe;
private LinearLayout mRelSelectAll;
private ImageView mIconSelectAll;
private TextView mTxtSelectAll;
private LinearLayout mRelRelatedDown;
private Button mBtnExit;
private ImageButton mBtnSubmenu;
private TextView mTxtMenutitle;
private ImageView mImgDetail;
private ImageView mImgFavorite;
private ListView mListView;
private GridView mGridView;
private ContentDetailListAdapter mListAdapter;
private long mContentId = 0;
private String mThumbnailNormalPath;
private String mContentName;
private String mContentDetail;
private String mDeliveryStartDate;
private String mDeliveryEndDate;
private String mLastDeliveryDate;
private boolean mDownloadedFlg;
private boolean mDownloadingFlg;
private boolean mUpdatedFlg;
private boolean mPausedFlg;
private boolean mPdfSendMailFlg;
private boolean mPrintFlg;
private boolean mReaderShareFlg;
private String mContentType;
private int mContractContentId;
private int mContentAlertLevel;
private int mAllPageNum;
private int mContentSize;
private ImageView mImgMail;
private ImageView mImgPrint;
private ImageView mImgAlert;
private ImageView mImgShare;
private ArrayList<String>mGroupInfo;
private ArrayList<String>mCategoryInfo;
private ArrayList<ContentPageSerializableDto> mPages;
private JSONArray mContentLinks;
private ContentDetailLinkAdapter mLinkAdapter;
private List<ContentLinkAdapterDto> mLinkListItem;
private boolean contentLinkFlag;
private ContractDto mContractDto;
private ContentDto mContentDto;
private String mFromClassName;
private int mSubMenuItemCount = -1; //サブメニューの項目の数
@Override
@SuppressWarnings("unchecked")
public void onCreate(Bundle savedInstanceState) {
Logger.i(TAG, "onCreate");
super.onCreate(savedInstanceState);
setContentView(R.layout.ac_content_detail_view);
Intent intent = getIntent();
mContentId = intent.getLongExtra("contentId",0);
mContentDto = AbstractDao.getDao(ContentDao.class).getContent(mContentId); // TODO: この下のフィールドも全部これを使ってIntent渡しをやめる
mThumbnailNormalPath = intent.getStringExtra("ThumbnailNormalPath");
mContentName = intent.getStringExtra("ContentName");
mContentDetail = intent.getStringExtra("ContentDetail");
mDeliveryStartDate = intent.getStringExtra("DeliveryStartDate");
mDeliveryEndDate = intent.getStringExtra("DeliveryEndDate");
mDownloadedFlg = intent.getBooleanExtra("DownloadedFlg", false);
mDownloadingFlg = intent.getBooleanExtra("DownloadingFlg", false);
mUpdatedFlg = intent.getBooleanExtra("UpdatedFlg", false); //2012 12 17 Contents Link 수정
mPages = (ArrayList<ContentPageSerializableDto>)intent.getSerializableExtra("Pages");
mAllPageNum = intent.getIntExtra("AllPageNum", 0);
mContentSize = intent.getIntExtra("ContentSize", 0);
mGroupInfo = intent.getStringArrayListExtra("GroupInfo");
mCategoryInfo = intent.getStringArrayListExtra("CategoryInfo");
mPausedFlg = getIntent().getBooleanExtra("PausedFlg", false);
mPdfSendMailFlg = getIntent().getBooleanExtra("PdfSendMailFlg", false);
mPrintFlg = getIntent().getBooleanExtra("PrintFlg", false);
mReaderShareFlg = getIntent().getBooleanExtra("ReaderShareFlg", false);
// 콘텐츠 링크에서 넘어왔는지 값을 체크
// コンテンツリンクからの遷移か否か
contentLinkFlag = getIntent().getBooleanExtra("ContentLinkFlag", false);
mLastDeliveryDate = getIntent().getStringExtra("LastDeliveryDate");
mContentType = getIntent().getStringExtra("ContentType");
mContractContentId = getIntent().getIntExtra("ContractContentId", 0);
mContentAlertLevel = getIntent().getIntExtra("ContentAlertLevel", 0);
mFromClassName = getIntent().getStringExtra("FromClassName");
mBtnExit = (Button)findViewById(R.id.btn_exit);
mBtnSubmenu = (ImageButton)findViewById(R.id.btn_submenu);
mTxtMenutitle = (TextView)findViewById(R.id.txt_detail_menu_title);
mImgDetail = (ImageView)findViewById(R.id.img_detail_main);
mImgFavorite = (ImageView)findViewById(R.id.img_favorite);
mBtnDownloadStatus = (Button)findViewById(R.id.btn_download_status);
mBtnOpenStatus = (Button)findViewById(R.id.btn_content_open_status);
mBtnStreaming = (Button)findViewById(R.id.btn_content_streaming);
mBtnInappPurchase = (Button)findViewById(R.id.btn_inapp_purchase);
mBtnSubscribe = (Button)findViewById(R.id.btn_subscribe);
mBtnDelete = (Button)findViewById(R.id.btn_content_delete);
mTxtDeliveryDateStart = (TextView)findViewById(R.id.txt_delivery_date_start);
mTxtDeliveryDateEnd = (TextView)findViewById(R.id.txt_delivery_date_end);
//サブメニューの項目が「コンテンツ削除う」以外にない場合サブメニュー非表示
if (getRInteger(R.integer.hide_delete_content) == 1) {
final ContentDto contentDto = contentDao.getContent(mContentId);
final List<ContentCheckListTypeItem> members = createSubMenuItems(contentDto, false);
mSubMenuItemCount = members.size();
if (mSubMenuItemCount == 0) {
mBtnSubmenu.setVisibility(View.INVISIBLE);
}
}
findViewById(R.id.linear_publisher_info).setVisibility(View.GONE);
mTxtLastDeliveryDateValue = (TextView)findViewById(R.id.txt_update_date_value);
mTxtContentSizeValue = (TextView)findViewById(R.id.txt_content_size_value);
mTxtContractContentId = (TextView)findViewById(R.id.txt_content_num);
mTxtAllPageNum = (TextView)findViewById(R.id.txt_all_page_num);
mRelSelectAll = (LinearLayout)findViewById(R.id.rel_select_all);
mIconSelectAll = (ImageView)findViewById(R.id.icon_select_all);
mTxtSelectAll = (TextView)findViewById(R.id.txt_select_all);
mRelRelatedDown = (LinearLayout)findViewById(R.id.rel_related_down);
mRelSelectAll.setVisibility(View.GONE);
mRelRelatedDown.setVisibility(View.GONE);
mGridView = (GridView)findViewById(R.id.grid_link_view);
if (mDeliveryStartDate != null) {
mDeliveryStartDate = mDeliveryStartDate.replaceAll("\\s", " ");
mDeliveryStartDate = mDeliveryStartDate + "~";
}
if(mDeliveryEndDate!=null){
mDeliveryEndDate = mDeliveryEndDate.replaceAll("\\s", " ");
mTxtDeliveryDateEnd.setText(mDeliveryEndDate);
mLastDeliveryDate = mLastDeliveryDate.replaceAll("\\s", " ");
}
mTxtDeliveryDateStart.setText(mDeliveryStartDate);
mTxtLastDeliveryDateValue.setText(mLastDeliveryDate);
setTabHost();
mListView = (ListView)findViewById(id.list_detail);
LayoutInflater inflater = (LayoutInflater)getApplication().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
LinearLayout headLayout = (LinearLayout)inflater.inflate(R.layout.content_detail_info, null);
mImgAlert = (ImageView)headLayout.findViewById(R.id.img_alert);
mImgMail = (ImageView)headLayout.findViewById(R.id.img_mail);
mImgPrint = (ImageView)headLayout.findViewById(R.id.img_print);
mImgShare = (ImageView)headLayout.findViewById(R.id.img_share);
if(mContentAlertLevel !=0 ){
mImgAlert.setVisibility(View.VISIBLE);
}
if(mPdfSendMailFlg){
mImgMail.setVisibility(View.VISIBLE);
}
if(mPrintFlg){
mImgPrint.setVisibility(View.VISIBLE);
}
boolean showImgShareFlg = false;
if (Options.getInstance(this).getHomeMenuGroup() == 0) {
headLayout.findViewById(R.id.linear_group).setVisibility(View.GONE);
}
if (Options.getInstance(this).getHomeMenuCategory() == 0) {
headLayout.findViewById(R.id.linear_category).setVisibility(View.GONE);
}
if (mContentAlertLevel == 0 && !mPdfSendMailFlg) {
headLayout.findViewById(R.id.linear_detail_icons).setVisibility(View.GONE);
if (Options.getInstance(this).getHomeMenuGroup() == 0 && Options.getInstance(this).getHomeMenuCategory() == 0) {
headLayout.findViewById(R.id.linear1).setVisibility(View.GONE);
}
}
mListView.addHeaderView(headLayout);
Logger.d(TAG, "content_link_status : " + contentLinkFlag);
if (Options.getInstance(this).getFilerFavorite() == 1
&& !ABVDataCache.getInstance().serviceOption.isPayment()) {
mImgFavorite.setVisibility(View.VISIBLE);
if (contentLogic.isExsitContentFavorite(mContentId)) {
mImgFavorite.setBackgroundResource(R.drawable.icon_favorite_add_on);
} else {
mImgFavorite.setBackgroundResource(R.drawable.icon_favorite_add_off);
}
} else {
mImgFavorite.setVisibility(View.GONE);
}
// Newフラグ更新
if (mContentDto.newFlg) {
mContentDto.newFlg = false;
contentDao.updateContent(mContentDto, false);
}
if (ABVDataCache.getInstance().serviceOption.isPayment()) {
BillingHelper.getInstance().init(this);
}
Resources r = Resources.getSystem();
Configuration config = r.getConfiguration();
onConfigurationChanged(config);
}
//サブメニューの項目が「コンテンツ削除」以外にない場合サブメニュー非表示
private void checkSubMenu() {
if (getRInteger(R.integer.hide_delete_content) == 1 && mSubMenuItemCount == 0) {
mBtnSubmenu.setVisibility(View.INVISIBLE);
} else {
mBtnSubmenu.setVisibility(View.VISIBLE);
}
}
private void setTabHost() {
mTabHost = (TabHost)findViewById(R.id.tab_content_detail);
mTabHost.setup();
mTabHost.setCurrentTab(0);
View tabView1 = createTabView(this, getResources().getString(R.string.detail_info), "info", true);
View tabView2 = createTabView(this, getResources().getString(R.string.related_content), "link", false);
TabSpec tInfo = mTabHost.newTabSpec("info")
.setIndicator(tabView1)
.setContent(R.id.tab_content_info);
TabSpec tLink = mTabHost.newTabSpec("link")
.setIndicator(tabView2)
.setContent(R.id.tab_link_content);
mTabHost.addTab(tInfo);
mTabHost.addTab(tLink);
final Button tInfoBtn = (Button) mTabHost.getTabWidget().getChildAt(0).findViewById(R.id.tabs_image);
final Button tLinkBtn = (Button) mTabHost.getTabWidget().getChildAt(1).findViewById(R.id.tabs_image);
mTabHost.setOnTabChangedListener(new OnTabChangeListener() {
@Override
public void onTabChanged(String tabId) {
if(tabId.equals("info")){
mRelRelatedDown.setVisibility(View.GONE);
mRelSelectAll.setVisibility(View.GONE);
tInfoBtn.setTextColor(Color.WHITE);
tLinkBtn.setTextColor(Color.BLACK);
}else if(tabId.equals("link")){
if(mLinkAdapter.getCheckableCount() != 0){
mRelRelatedDown.setVisibility(View.VISIBLE);
mRelSelectAll.setVisibility(View.VISIBLE);
}
tInfoBtn.setTextColor(Color.BLACK);
tLinkBtn.setTextColor(Color.WHITE);
}
}
});
}
private View createTabView(Context context, String tabText, final String tag, boolean pressed) {
View view = LayoutInflater.from(context).inflate(R.layout.custom_tab, null, false);
Button imgButton = (Button)view.findViewById(R.id.tabs_image);
imgButton.setBackgroundResource(R.drawable.icon_contentdetail_tab);
imgButton.setText(tabText);
if (!pressed) {
imgButton.setTextColor(Color.BLACK);
}
imgButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (tag.equals("info")) {
mTabHost.setCurrentTab(0);
}else{
mTabHost.setCurrentTab(1);
}
}
});
return view;
}
/** 
* ボタンイベント設定
* @since 1.0.0
*/
private void setOnButtonEvent() {
mBtnExit.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (contentLinkFlag && contentDownloader.getActiveCountWithoutWaiting() > 0) {
showDownloadPauseDialog();
}
else {
finish();
}
}
});
mBtnSubmenu.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mDownloadedFlg) {
showSubMenuDialog(v);
}
}
});
mImgFavorite.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
if (contentLogic.isExsitContentFavorite(mContentId)) {
contentLogic.setFavoriteContent(mContentId, false);
mImgFavorite.setBackgroundResource(R.drawable.icon_favorite_add_off);
Logger.d(TAG, "icon_favorite_add_off click");
} else {
contentLogic.setFavoriteContent(mContentId, true);
mImgFavorite.setBackgroundResource(R.drawable.icon_favorite_add_on);
Logger.d(TAG, "icon_favorite_add_on click");
}
} catch (Exception e) {
Logger.e(TAG, "mImgFavorite onClick", e);
handleErrorMessageToast(ErrorCode.ERROR);
}
}
});
}
/** 
* 項目設定
* @since 1.0.0
*/
private void setDetal() {
int resizeLength;
if ((getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT)) {
// 縦の場合、画面の5分の2の80%を基準にリサイズ
resizeLength = (int)((getResources().getDisplayMetrics().widthPixels) * 0.4);
}else{
// 横の場合、画面の半分の80%を基準にリサイズ
resizeLength = (int)((getResources().getDisplayMetrics().heightPixels) * 0.5);
}
Bitmap resized = BitmapUtil.getResizedBitmap(mContentDto.thumbnailBigPath, resizeLength, resizeLength, Config.RGB_565);
if (resized == null) {
resized = BitmapUtil.getResizedBitmap(mThumbnailNormalPath, resizeLength, resizeLength, Config.RGB_565);
}
mImgDetail.setImageBitmap(resized);
mImgDetail.setAdjustViewBounds(true);
mImgDetail.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
mImgDetail.setPadding(0, 0, 1, 1);
if(ABVDataCache.getInstance().serviceOption.isPayment()){
mImgDetail.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mBtnExit.setVisibility(View.INVISIBLE);
findViewById(R.id.big_thumbnail_layout).setVisibility(View.VISIBLE);
}
});
findViewById(R.id.big_thumbnail_close).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mBtnExit.setVisibility(View.VISIBLE);
findViewById(R.id.big_thumbnail_layout).setVisibility(View.GONE);
}
});
try {
String contentInfoFilePath = ABVEnvironment.getInstance().getContentDirectoryPath(mContentId, false) + File.separator + ABVEnvironment.ContentVersionInfoJsonName;
DownloadedContentInfoJSON json = ContentFileUtil.getDownloadedContentInfoJSON(contentInfoFilePath);
String largeTthumbnailPath = ABVEnvironment.getInstance().getContentDetailDirectoryPath(mContentId, false) + File.separator + json.thumbnailLargeName;
Bitmap largeBitmap = BitmapUtil.getBitmap(largeTthumbnailPath, Config.RGB_565);
if (largeBitmap == null) {
largeBitmap = BitmapUtil.getBitmap(mContentDto.thumbnailBigPath, Config.RGB_565);
}
if (largeBitmap == null) {
largeBitmap = BitmapUtil.getBitmap(mContentDto.thumbnailNormalPath, Config.RGB_565);
}
if (largeBitmap != null) {
((ImageView) findViewById(R.id.big_thumbnail)).setImageBitmap(largeBitmap);
}
} catch (Exception e) {
Logger.e(TAG, e.toString());
}
}
if (ABVDataCache.getInstance().serviceOption.isPayment()) {
mTxtMenutitle.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);
} else {
int drawableId;
if (mContentType.equals(ContentJSON.KEY_PDF_TYPE)) {
drawableId = R.drawable.icon_pdf_file;
} else if (mContentType.equals(ContentJSON.KEY_IMAGE_TYPE)) {
drawableId = R.drawable.icon_image_file;
} else if (mContentType.equals(ContentJSON.KEY_MOVIE_TYPE)) {
drawableId = R.drawable.icon_movie_file;
} else if (mContentType.equals(ContentJSON.KEY_PANO_MOVIE_TYPE)) {
drawableId = R.drawable.icon_pano_movie_file;
} else if (mContentType.equals(ContentJSON.KEY_PANO_IMAGE_TYPE)) {
drawableId = R.drawable.icon_pano_image_file;
} else if (mContentType.equals(ContentJSON.KEY_OBJECTVR_TYPE)) {
drawableId = R.drawable.icon_objectvr_file;
} else if (mContentType.equals(ContentJSON.KEY_MUSIC_TYPE)) {
drawableId = R.drawable.icon_music_file;
} else if (mContentType.equals(ContentJSON.KEY_HTML_TYPE)) {
drawableId = R.drawable.icon_html_file;
} else if (mContentType.equals(ContentJSON.KEY_OTHER_TYPE)) {
drawableId = R.drawable.icon_other_file;
} else if (mContentType.equals(ContentJSON.KEY_LINK_TYPE)) {
drawableId = R.drawable.icon_weblink_file;
} else if (mContentType.equals(ContentJSON.KEY_ENQUETE_TYPE)) {
drawableId = R.drawable.icon_enquete_file;
} else if (mContentType.equals(ContentJSON.KEY_EXAM_TYPE)) {
drawableId = R.drawable.icon_exam_file;
} else {
drawableId = R.drawable.icon_no_file;
}
mTxtMenutitle.setCompoundDrawablesWithIntrinsicBounds(drawableId, 0, 0, 0);
}
findViewById(R.id.txt_content_num_info).setVisibility(View.VISIBLE);
mTxtContractContentId.setText("" + mContractContentId);
mTxtContentSizeValue.setText(makeContentSizeStr(mContentSize));
if (isNormalSize()) {
mGridView.setNumColumns(1);
}else{
mGridView.setNumColumns(2);
}
mTxtAllPageNum.setText(""+mAllPageNum);
}
private String makeContentSizeStr(int contentSize) {
StringBuffer sb = new StringBuffer();
if (contentSize == 0) {
// 0の場合1KB
sb.append("1KB");
} else {
if (contentSize >= 1024) {
int contentSizeMB = contentSize / 1024;
sb.append(StringUtil.numberDispFormatComma(contentSizeMB));
sb.append("MB");
} else {
sb.append(StringUtil.numberDispFormatComma(contentSize));
sb.append("KB");
}
}
return sb.toString();
}
private void setDownloadStatus() {
mBtnInappPurchase.setVisibility(View.GONE);
mBtnSubscribe.setVisibility(View.GONE);
mBtnDelete.setVisibility(View.GONE);
mBtnStreaming.setVisibility(View.GONE);
mBtnDownloadStatus.setVisibility(View.GONE);
mBtnOpenStatus.setVisibility(View.GONE);
mBtnSubmenu.setVisibility(View.INVISIBLE);
if (contentLinkFlag) {
return;
}
if (mDownloadingFlg && !mPausedFlg) {
mBtnDownloadStatus.setVisibility(View.VISIBLE);
mBtnDownloadStatus.setText(getResources().getString(R.string.downloading));
mBtnDownloadStatus.setCompoundDrawablesWithIntrinsicBounds(getResources().getDrawable(R.drawable.ic_download_pause), null, null, null);
mBtnDownloadStatus.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//일시정지 이벤트
Logger.d(TAG, "#####################pause");
contentDownloadPause(mContentId);
mPausedFlg = true;
mDownloadingFlg = false;
setDownloadStatus();
}
});
} else if (!mDownloadedFlg && !mPausedFlg && !isStreamingOnly(mContentDto)) {
mBtnDownloadStatus.setVisibility(View.VISIBLE);
mBtnDownloadStatus.setText(getResources().getString(R.string.download));
mBtnDownloadStatus.setCompoundDrawablesWithIntrinsicBounds(getResources().getDrawable(R.drawable.ic_download), null, null, null);
mBtnDownloadStatus.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//다운로드 시작
Logger.d(TAG, "#####################start");
contentValidCheckAndDownload(mContentId);
mDownloadingFlg = true;
setDownloadStatus();
}
});
} else if (mDownloadedFlg && mUpdatedFlg && !mPausedFlg && !isStreamingOnly(mContentDto)) {
mBtnDownloadStatus.setVisibility(View.VISIBLE);
mBtnDownloadStatus.setText(getResources().getString(R.string.update));
mBtnDownloadStatus.setCompoundDrawablesWithIntrinsicBounds(getResources().getDrawable(R.drawable.ic_download_update), null, null, null);
mBtnDownloadStatus.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//업데이트 시작
Logger.d(TAG, "#####################update");
contentValidCheckAndDownload(mContentId);
mDownloadingFlg = true;
setDownloadStatus();
}
});
mBtnOpenStatus.setVisibility(View.VISIBLE);
mBtnOpenStatus.setText(getResources().getString(R.string.open));
mBtnOpenStatus.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//콘텐츠 재생
Logger.d(TAG, "#####################open");
startContentViewActivity(mContentId);
}
});
checkSubMenu();
} else if (mDownloadedFlg && !mUpdatedFlg) {
mBtnOpenStatus.setVisibility(View.VISIBLE);
mBtnOpenStatus.setText(getResources().getString(R.string.open));
mBtnOpenStatus.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//콘텐츠 재생
startContentViewActivity(mContentId);
}
});
checkSubMenu();
if (ABVDataCache.getInstance().serviceOption.isPayment()) {
mBtnDelete.setVisibility(View.VISIBLE);
mBtnDelete.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
showContentDeleteDialog(mContentDto);
}
});
}
}else if (mPausedFlg) {
mBtnDownloadStatus.setVisibility(View.VISIBLE);
mBtnDownloadStatus.setText(R.string.restart);
mBtnDownloadStatus.setCompoundDrawablesWithIntrinsicBounds(getResources().getDrawable(R.drawable.ic_download_resume), null, null, null);
mBtnDownloadStatus.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
resumeContentDownload(mContentId);
mDownloadingFlg = true;
mPausedFlg = false;
setDownloadStatus();
}
});
mBtnOpenStatus.setVisibility(View.VISIBLE);
mBtnOpenStatus.setText(getResources().getString(R.string.cancel));
mBtnOpenStatus.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ContentDto contentDto = contentDao.getContent(mContentId);
contentDownloadCancel(contentDto);
mPausedFlg = false;
mDownloadingFlg = false;
setDownloadStatus();
}
});
}
if (isStreamingEnable(mContentDto)) {
mBtnStreaming.setVisibility(View.VISIBLE);
mBtnStreaming.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startStreamingActivity(mContentId);
}
});
}
if (isNormalSize()) { // 携帯画面の場合、ボタンに納まらないので数が多い場合、文字を短くもしくはアイコンのみにする。上にスペースがあるが修正するとLayoutの全面見直しが必要。
int visibleCount = getVisibleCount(mBtnOpenStatus, mBtnDownloadStatus, mBtnStreaming);
if (visibleCount == 2) {
if (mDownloadedFlg && mUpdatedFlg && !mPausedFlg && !isStreamingOnly(mContentDto)) {
mBtnDownloadStatus.setText("Update");
}
else {
mBtnDownloadStatus.setText("DL");
}
}
else if (visibleCount > 2) {
mBtnOpenStatus.setText("");
mBtnDownloadStatus.setText("");
mBtnStreaming.setText("");
}
}
if (ABVDataCache.getInstance().serviceOption.isPayment()) {
mBtnSubmenu.setVisibility(View.GONE);
if (mContentDto.downloadedFlg) { // DL済みの場合
}
else if (mContentDto.downloadingFlg) { // DL中の場合
}
else if (!mContentDto.payFlg // 無料の場合
|| AbstractLogic.getLogic(ContentLogic.class).isAllSubscribed(mContentDto) // 定期購読済み
|| !StringUtil.isNullOrEmpty(mContentDto.productId) && mContentDto.purchaseState == 0) { // 単品購入済み
}
else {
mBtnOpenStatus.setVisibility(View.GONE);
mBtnDownloadStatus.setVisibility(View.GONE);
mBtnInappPurchase.setVisibility(View.VISIBLE);
mBtnInappPurchase.setText(mContentDto.price);
mBtnInappPurchase.setFocusable(false);
mBtnInappPurchase.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
BillingHelper.getInstance().buy(v, mContentDto.productId);
} catch (Exception e) {
Logger.e(TAG, "inapp launchPurchaseFlow failed.", e);
Toast.makeText(ContentDetailViewActivity.this, R.string.billing_error_purchase_failed, Toast.LENGTH_SHORT).show();
}
}
});
}
}
}
private int getVisibleCount(Button... buttons) {
int count = 0;
for (Button button : buttons) {
if (button.getVisibility() == View.VISIBLE) {
count++;
}
}
return count;
}
/** 
* リスト画面設定
* @since 1.0.0
*/
private void setDetailList() {
mTxtMenutitle.setText(mContentName);
mTxtMenutitle.requestFocus();
// コンテンツリンク情報読込
try {
mContentLinks = loadContentLinksJSON(mContentId);
} catch (Exception e) {
Logger.e(TAG, "loadContentLinksJSON", e);
ABVToastUtil.showMakeText(getApplicationContext(), R.string.ERROR, Toast.LENGTH_SHORT);
}
mListAdapter = new ContentDetailListAdapter(this, R.layout.item_content_detail_render, mPages);
mListView.setAdapter(mListAdapter);
mListView.setDivider(null);
mTxtContentDetail = (TextView) findViewById(R.id.txt_content_detail);
mTxtGroupInfo = (TextView) findViewById(R.id.txt_group_info);
mTxtCategoryInfo = (TextView) findViewById(R.id.txt_category_info);
String groupInfo = null;
for (int i=0; i < mGroupInfo.size(); i++) {
if (i==0) {
groupInfo = mGroupInfo.get(i);
}else{
groupInfo += ", " + mGroupInfo.get(i);
}
}
String categoryInfo = null;
for (int i=0; i < mCategoryInfo.size(); i++) {
if (i==0) {
categoryInfo = mCategoryInfo.get(i);
}else{
categoryInfo += ", " + mCategoryInfo.get(i);
}
}
if(ABVDataCache.getInstance().serviceOption.isPayment()){
((TextView) findViewById(R.id.txt_content_explain)).setText(mContentDetail);
findViewById(R.id.sc_content_explain).setVisibility(View.VISIBLE);
findViewById(R.id.contentNumberLayout).setVisibility(View.INVISIBLE);
findViewById(R.id.rightSideInfoLayout).setVisibility(View.GONE);
mTxtContentDetail.setVisibility(View.GONE);
} else {
mTxtContentDetail.setMinLines(1);
mTxtContentDetail.setText(mContentDetail);
findViewById(R.id.sc_content_explain).setVisibility(View.GONE);
findViewById(R.id.contentNumberLayout).setVisibility(View.VISIBLE);
findViewById(R.id.rightSideInfoLayout).setVisibility(View.VISIBLE);
mTxtContentDetail.setVisibility(View.VISIBLE);
}
mTxtGroupInfo.setText(groupInfo);
mTxtCategoryInfo.setText(categoryInfo);
if (mContentLinks == null || ABVDataCache.getInstance().serviceOption.isPayment()) {
mTabHost.getTabWidget().getChildAt(1).setVisibility(View.GONE);
}else{
ProjectLogic mProjectLogic = AbstractLogic.getLogic(ProjectLogic.class);
boolean isCommonContentForSelf = mProjectLogic.isCommonContent(mContentId);
mLinkListItem = new ArrayList<>();
for (int i = 0; i < mContentLinks.length(); i++) {
ContentLinkJSON contentLinkJSON = new ContentLinkJSON(mContentLinks.getJSONObject(i));
ContentLinkAdapterDto contentLinkDto = new ContentLinkAdapterDto();
makeContentLinkDto(contentLinkDto, contentLinkJSON.getContentId(), contentLinkJSON.getContentName()); // 上でjsonのValidationを行っているのでここではないはず
if (isCommonContentForSelf && !mProjectLogic.isCommonContent(contentLinkDto.contentId)) {
continue;
}
if (contentLinkDto.deliveryStartDate == null || DateTimeUtil.getCurrentDate().after(contentLinkDto.deliveryStartDate)) {
mLinkListItem.add(contentLinkDto);
}
}
if(mLinkListItem.size() == 0){
mTabHost.getTabWidget().getChildAt(1).setVisibility(View.GONE);
} else {
mLinkAdapter = new ContentDetailLinkAdapter(this, mLinkListItem, new ContentDetailLinkAdapter.ContentLinkAdapterListener() {
@Override
public void onAllCheckChanged(boolean allChecked) {
if(mLinkAdapter.getCheckableCount() != 0){
if (allChecked) {
mIconSelectAll.setBackgroundResource(R.drawable.icon_content_detail_check_deselected);
mTxtSelectAll.setText(R.string.allDelete);
} else {
mIconSelectAll.setBackgroundResource(R.drawable.icon_content_detail_check_selected);
mTxtSelectAll.setText(R.string.allSelect);
}
}
}
@Override
public boolean OnDownloadTransfer(long contentId, ContentLinkAdapterDto dto) {
boolean result = true;
//관련콘텐츠의 다운로드 처리
// 関連コンテンツのダウンロード処理
switch (dto.contentStatus) {
case IMPOSSIBLE:
break;
case DONE:
break;
case POSSIBLE:
if (dto.selectable) {
ContentDto contentDto = AbstractDao.getDao(ContentDao.class).getContent(contentId);
contentOpenOrDownload(contentDto);
} else {
contentValidCheckAndDownload(contentId, false);
dto.contentStatus = ContentStatus.DOWNLOADING;
dto.checkable = false;
}
break;
case DOWNLOADING:
break;
case WAITING:
resumeContentDownload(contentId);
break;
case STREAMING:
break;
default:
break;
}
setRelRelatedDownVisibility();
return result;
}
@Override
public void onViewTransfer(long contentId) {
startContentViewActivity(contentId);
}
});
mRelSelectAll.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mLinkAdapter.allCheck) {
mIconSelectAll.setBackgroundResource(R.drawable.icon_content_detail_check_selected);
mTxtSelectAll.setText(R.string.allSelect);
} else {
mIconSelectAll.setBackgroundResource(R.drawable.icon_content_detail_check_deselected);
mTxtSelectAll.setText(R.string.allDelete);
}
mLinkAdapter.allCheck();
mLinkAdapter.notifyDataSetChanged();
}
});
//관련 콘텐츠 전체 다운로드 처리
// 関連コンテンツの全体ダウンロード処理
mRelRelatedDown.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mLinkAdapter.getCheckedCount() == 0) {
ABVToastUtil.showMakeText(ContentDetailViewActivity.this, R.string.link_non_selected, Toast.LENGTH_SHORT);
}else{
List<ContentLinkAdapterDto> pausedList = mLinkAdapter.getPausedList();
if (pausedList.size() > 0) {
for (ContentLinkAdapterDto dto : pausedList) {
for (ContentLinkAdapterDto linkDto : mLinkListItem) {
if (linkDto.contentId == dto.contentId) {
linkDto.contentStatus = ContentStatus.DOWNLOADING;
mLinkAdapter.notifyDataSetChanged();
resumeContentDownload(dto.contentId);
}
}
}
}
long[] downloadIds = mLinkAdapter.getDownloadList();
startContentDownload(downloadIds);
}
}
});
mGridView.setAdapter(mLinkAdapter);
setRelRelatedDownVisibility();
}
}
}
/** 
* リスト画面のデータ設定
* @since 1.0.0
*/
private void contentDetailDown() {
setDetailList();
setOnButtonEvent();
setDetal();
setDownloadStatus();
if(mDownloadedFlg) {
checkSubMenu();
} else {
mBtnSubmenu.setVisibility(View.GONE);
}
}
@Override
public void onDownloadingContentZip(ContentZipDownloadNotification notification) {
super.onDownloadingContentZip(notification);
switch (notification.getDownloadStatus()) {
case Waiting:
break;
case Paused:
break;
case AutoPaused:
break;
case Initializing:
break;
case Failed:
break;
case None:
break;
case Downloading:
if (notification.getContentId() != mContentId && mLinkListItem != null) {
for (ContentLinkAdapterDto dto : mLinkListItem) {
if (dto.contentId == notification.getContentId() && dto.contentStatus != ContentStatus.DOWNLOADING) {
dto.contentStatus = ContentStatus.DOWNLOADING;
break;
}
}
runOnUiThread(new Runnable() {
@Override
public void run() {
mLinkAdapter.notifyDataSetChanged();
}
});
}
break;
case Succeeded:
if (notification.getContentId() == mContentId) {
mDownloadedFlg = true;
mDownloadingFlg = false;
mPausedFlg = false;
mUpdatedFlg = false;
runOnUiThread(new Runnable() {
@Override
public void run() {
setDownloadStatus();
}
});
} else {
long linkContentId = notification.getContentId();
if (mLinkListItem != null) {
for (ContentLinkAdapterDto dto : mLinkListItem) {
if (dto.contentId == linkContentId) {
makeContentLinkDto(dto, linkContentId, dto.contentName);
dto.contentStatus = ContentStatus.DONE;
dto.checkable = false;
}
}
}
runOnUiThread(new Runnable() {
@Override
public void run() {
setRelRelatedDownVisibility();
//プッシュメッセージを経由して、コンテンツダウンロードする場合対応
if (mLinkAdapter != null) {
mLinkAdapter.notifyDataSetChanged();
}
}
});
}
ActivityHandlingHelper.getInstance().setRequireHomeReload(true);
break;
case Canceled:
if (notification.getContentId() != mContentId) {
long linkContentId = notification.getContentId();
if (mLinkListItem != null) {
for (ContentLinkAdapterDto dto : mLinkListItem) {
if (dto.contentId == linkContentId) {
dto.contentStatus = ContentStatus.POSSIBLE;
dto.checkable = true;
}
}
}
runOnUiThread(new Runnable() {
@Override
public void run() {
setRelRelatedDownVisibility();
mLinkAdapter.notifyDataSetChanged();
}
});
}
break;
default:
break;
}
}
@Override
protected void onStop() {
super.onStop();
}
@Override
protected void onDestroy() {
super.onDestroy();
Logger.v(TAG, "onDestroy()");
}
@Override
public void onResume() {
Logger.v(TAG, "onResume()");
super.onResume();
mContentDto = AbstractDao.getDao(ContentDao.class).getContent(mContentId);
setDownloadStatus();
if (ABVDataCache.getInstance().serviceOption.isPayment() && BillingHelper.getInstance().requireInit()) {
BillingHelper.getInstance().init(this);
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
Logger.d("onActivityResult");
if (ABVDataCache.getInstance().serviceOption.isPayment()) {
if (BillingHelper.getInstance().handleActivityResult(requestCode, resultCode, data)) {
Log.d(TAG, "onActivityResult handled by IABUtil.");
}
else {
super.onActivityResult(requestCode, resultCode, data);
}
}
}
@Override
public void finish() {
// ホーム画面リロード
ActivityHandlingHelper.getInstance().setRequireHomeReload(true);
super.finish();
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
if (!isNormalSize()) {
int widthPixels = (int) (getResources().getDisplayMetrics().widthPixels * 0.9) ;
int heightPixels = (int) (getResources().getDisplayMetrics().heightPixels * 0.9);
WindowManager.LayoutParams lp = getWindow().getAttributes();
if (widthPixels > heightPixels) {
lp.width = heightPixels;
lp.height = heightPixels;
} else {
lp.width = widthPixels;
lp.height = widthPixels;
}
getWindow().setAttributes(lp);
}
contentDetailDown();
}
/**
* 指定したコンテンツIDのjson.txtの情報を返します。
* @param contentId
* @return JSONArray
* @throws IOException
* @throws AcmsException
*/
private JSONArray loadContentLinksJSON(long contentId) throws IOException, AcmsException {
DownloadedContentInfoJSON json = ContentFileUtil.getDownloadedContentInfoJSON(contentId);
return json.contentLinks;
}
/**
* コンテンツの状態をチェックしてステータスを返します。
* @param contentDto
* @return
* @throws Exception
*/
private ContentLinkAdapterDto.ContentStatus getContentStatus(ContentDto contentDto) {
// ローカルに存在しない場合、ダウンロード不可
if (contentDto == null) {
return ContentLinkAdapterDto.ContentStatus.IMPOSSIBLE;
}
// ダウンロード済みならば、ダウンロード済み
if (contentDto.downloadedFlg) {
return ContentLinkAdapterDto.ContentStatus.DONE;
}
if (isStreamingOnly(contentDto)) { // Streamingのみの場合
return ContentLinkAdapterDto.ContentStatus.STREAMING;
}
if (isStreamingEnable(contentDto)) { // Streamingが可能な場合、設定の優先に従って変更
int tapActionOnDeliverySelect = PreferenceUtil.getInt(this, DefPrefKey.TAP_ACTION_ON_DELIVERY_SELECT, "2");
switch (tapActionOnDeliverySelect) {
case DeliveryType.SELECT:
return ContentLinkAdapterDto.ContentStatus.POSSIBLE;
case DeliveryType.STREAMING:
return ContentLinkAdapterDto.ContentStatus.STREAMING;
}
}
// ダウンロードしていなければ、ダウンロード可能
if (!contentDto.downloadingFlg) {
return ContentLinkAdapterDto.ContentStatus.POSSIBLE;
}
//-------- 以下、ダウンロード中のコンテンツのみ --------//
if (contentDto.downloadProgress > 0) {
return ContentLinkAdapterDto.ContentStatus.DOWNLOADING;
} else {
return ContentLinkAdapterDto.ContentStatus.WAITING;
}
}
/**
* ContentLinkDtoを作成する。コンテンツIDとコンテンツの情報が取得できない時に表示するコンテンツ名を指定します。
* @param contentLinkDto
* @throws Exception
*/
private void makeContentLinkDto(ContentLinkAdapterDto contentLinkDto, long contentId, String contentName) {
contentLinkDto.contentName = contentName;
contentLinkDto.contentId = contentId;
ContentDto contentDto= contentDao.getContent(contentLinkDto.contentId);
contentLinkDto.contentStatus = getContentStatus(contentDto);
if (contentDto != null) {
contentLinkDto.contentName = contentDto.contentName;
contentLinkDto.thumbnailPath = contentDto.thumbnailNormalPath;
contentLinkDto.contentId = contentDto.contentId;
contentLinkDto.deliveryStartDate = contentDto.deliveryStartDate;
//jeonghun Test
contentLinkDto.checkable = !contentDto.downloadedFlg && !isStreamingOnly(contentDto) && !contentDto.downloadingFlg;
if (isStreamingEnable(contentDto) && PreferenceUtil.getInt(this, DefPrefKey.TAP_ACTION_ON_DELIVERY_SELECT, "2") == DeliveryType.SELECT) {
// Streaming選択が可能な場合、設定の優先に従って変更
contentLinkDto.selectable = true;
}
}
}
/**
* 複数指定したコンテンツIDをダウンロードします。
* ダウンロードできないコンテンツがある場合、スキップされます。
* ダウンロードできないコンテンツが一つでもある場合、最後にメッセージを表示します。
* @param downloadIds
*/
private void startContentDownload(final long[] downloadIds) {
try {
showWifiDisconnectAlert(R.string.C_E_SYSTEM_0005, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
boolean allContentStart = true;
try {
for (int i = 0; i < downloadIds.length; i++) {
if (contentValidCheck(downloadIds[i]) == ContentCheckResultType.SUCCESS) {
if (mLinkListItem != null) {
for (ContentLinkAdapterDto linkDto : mLinkListItem) {
if (linkDto.contentId == downloadIds[i]) {
linkDto.contentStatus = ContentStatus.DOWNLOADING;
linkDto.checkable = false;
contentDownloader.download(downloadIds[i]);
mLinkAdapter.notifyDataSetChanged();
}
}
}
} else {
allContentStart = false;
}
}
if (!allContentStart) {
ErrorMessage.showErrorMessageDialog(ContentDetailViewActivity.this, R.string.app_name, ErrorCode.M003);
}
} catch (NetworkDisconnectedException e) {
handleErrorMessageToast(ErrorCode.NETWORK);
} catch (Exception e) {
Logger.e(TAG, "startContentDownload failed.",e);
handleErrorMessageToast(ErrorCode.ERROR);
}
}
}, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
});
} catch (NetworkDisconnectedException e) {
handleErrorMessageToast(ErrorCode.NETWORK);
}
}
private void startReaderContentDownload(final long[] downloadIds) {
try {
showWifiDisconnectAlert(R.string.C_E_SYSTEM_0005, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
ArrayList<Long> successIds = readerContentDownload(mContractDto, downloadIds);
for (long contentId : successIds) {
if (mLinkListItem != null) {
for (ContentLinkAdapterDto linkDto : mLinkListItem) {
if (linkDto.contentId == contentId) {
linkDto.contentStatus = ContentStatus.DOWNLOADING;
linkDto.checkable = false;
mLinkAdapter.notifyDataSetChanged();
}
}
}
}
}
}, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
});
} catch (NetworkDisconnectedException e) {
ABVToastUtil.showMakeText(this, R.string.NETWORK, Toast.LENGTH_SHORT);
}
}
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
if (contentLinkFlag && contentDownloader.getActiveCountWithoutWaiting() > 0) {
showDownloadPauseDialog();
}
else {
finish();
}
}
return super.onKeyUp(keyCode, event);
}
//일시 정지 중인 콘텐츠를 상세화면에서도 다운로드 가능
private void resumeContentDownload(long downPausedIds) {
try {
contentDownloader.resume(downPausedIds);
} catch (NetworkDisconnectedException e) {
handleErrorMessageToast(ErrorCode.NETWORK);
} catch (AcmsException e) {
Logger.e(TAG, "AcmsException", e);
handleErrorMessageToast(e);
}
}
// サブメニュー表示
public void showSubMenuDialog(View anchor) {
final ContentDto contentDto = contentDao.getContent(mContentId);
final List<ContentCheckListTypeItem> members = createSubMenuItems(contentDto, false);
ArrayList<String> menuNameList = new ArrayList<>();
for(ContentCheckListTypeItem member :members){
menuNameList.add(member.name);
}
final ABVPopupListWindow popupWindow = new ABVPopupListWindow(this);
popupWindow.setWidth(getRDimensionSize(R.dimen.popup_size_large));
popupWindow.setRepresentNames(menuNameList);
popupWindow.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
switch (members.get(position).id) {
case SubMenuType.CONTENT_DELETE:
showContentDeleteDialog(contentDto);
break;
case SubMenuType.USER_INFO_COPY:
getABVUIDataCache().srcContentId = contentDto.contentId;
ABVToastUtil.showMakeText(ContentDetailViewActivity.this, AndroidStringUtil.format(ContentDetailViewActivity.this, R.string.complete_state, R.string.copy), Toast.LENGTH_SHORT);
break;
case SubMenuType.USER_INFO_PASTE:
copyUserInfoCheckOrientation(getABVUIDataCache().srcContentId, contentDto.contentId);
break;
}
popupWindow.dismiss();
}
});
popupWindow.showAsDropDown(anchor);
}
public void showContentDeleteDialog(final ContentDto contentDto) {
ABookAlertDialog contentsDeleteDialog = AlertDialogUtil.deleteContentAlertDialog(this);
contentsDeleteDialog.setNegativeButton(R.string.cancel, null);
contentsDeleteDialog.setPositiveButton(R.string.confirm, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
if (contentDelete(contentDto)) {
// ダウンロードボタンの表示変更
mPausedFlg = false;
mDownloadingFlg = false;
mDownloadedFlg = false;
setDownloadStatus();
}
}
});
showAlertDialog(contentsDeleteDialog);
}
private void showDownloadPauseDialog() {
ABookAlertDialog contentsDeleteDialog = AlertDialogUtil.createAlertDialog(this, getString(R.string.confirm), getString(R.string.confirm_download_pause));
contentsDeleteDialog.setNegativeButton(R.string.cancel, null);
contentsDeleteDialog.setPositiveButton(R.string.confirm, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
contentDownloader.pauseAll(); // FIXME: なぜここですべてPauseにしている
finish();
}
});
showAlertDialog(contentsDeleteDialog);
}
private void setRelRelatedDownVisibility(){
if (mLinkAdapter == null || mLinkAdapter.getCheckableCount() == 0) {
mRelSelectAll.setVisibility(View.GONE);
mRelRelatedDown.setVisibility(View.GONE);
}
}
/**
* 発行者コンテンツの表示・非表示
*
* @param view
*/
public void onClickPublisherAll(View view) {
Logger.d(TAG, "onClickPublisherAll: %s", view.getId());
}
}
......@@ -176,7 +176,6 @@ public class GuideViewActivity extends ABVContentViewActivity {
private ContentJSON mContentJSON; // コンテンツのJSONデータ
private PDFIndexInfoJSON mPDFIndexInfoJSON; // PDFインデックスのJSONデータ
private PDFLinkJSON mPDFLinkJSON; // PDFリンクのJSONデータ
private PageInfoJSON mPageInfoJSON; // オーサリング画面の各ページサイズのJSONデータ
private Size mBaseFileSize;
......
package jp.agentec.abook.abv.ui.home.adapter;
import java.util.ArrayList;
import java.util.List;
import jp.agentec.abook.abv.cl.util.BitmapUtil;
import jp.agentec.abook.abv.launcher.android.R;
import jp.agentec.abook.abv.ui.common.activity.ABVAuthenticatedActivity;
import jp.agentec.abook.abv.ui.home.dto.ContentLinkAdapterDto;
import jp.agentec.abook.abv.ui.home.dto.ContentLinkAdapterDto.ContentStatus;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.CompoundButton;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.ToggleButton;
/**
* @author Chae
*
*/
public class ContentDetailLinkAdapter extends BaseAdapter {
private Context context;
private LayoutInflater inflater;
private List<ContentLinkAdapterDto> listItem;
public boolean allCheck = false;
private ContentLinkAdapterListener listener;
private final int mThumbnailSize;
public interface ContentLinkAdapterListener {
/**
* コンテンツリストが全てチェックの状態になった場合<br>
* または、コンテンツリストが全てチェックの状態じゃなくなった場合に呼び出されます。
* @param allChecked true:全てチェック、false:全てチェックではない
*/
void onAllCheckChanged(boolean allChecked);
boolean OnDownloadTransfer(long contentId, ContentLinkAdapterDto dto);
void onViewTransfer(long contentId);
}
public ContentDetailLinkAdapter(Context context, List<ContentLinkAdapterDto> listItem , ContentLinkAdapterListener listener) {
this.context = context;
inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mThumbnailSize = context.getResources().getDimensionPixelSize(R.dimen.detail_view_thumbnail_size);
this.listItem = listItem;
this.listener = listener;
}
@Override
public int getCount() {
return listItem.size();
}
@Override
public ContentLinkAdapterDto getItem(int position) {
return listItem.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (null == convertView) {
convertView = inflater.inflate(R.layout.item_content_detail_link_render, null);
}
final ContentLinkAdapterDto dto = listItem.get(position);
// int heightPixels = (int) (context.getResources().getDisplayMetrics().heightPixels * 0.4 * 0.55 * 0.5);
// LinearLayout mainLayout = (LinearLayout)convertView.findViewById(R.id.linear_detail_render);
// mainLayout.getLayoutParams().height = heightPixels;
ImageView imgThumbnail = (ImageView)convertView.findViewById(R.id.img_thumbnail);
if (imgThumbnail.getDrawable() != null) {
((BitmapDrawable) imgThumbnail.getDrawable()).getBitmap().recycle();
}
Bitmap resized = BitmapUtil.getResizedBitmap(listItem.get(position).thumbnailPath, mThumbnailSize, mThumbnailSize, Config.RGB_565);
// nullの場合サムネイルなしイメージを表示
if (resized == null || dto.contentStatus == ContentStatus.IMPOSSIBLE) {
resized = BitmapUtil.getResizedBitmapResource(context.getResources(), R.drawable.not_exist_thumbnail, 120, 120, Config.RGB_565);
}
imgThumbnail.setImageBitmap(resized);
imgThumbnail.setPadding(0, 0, 1, 1);
TextView tvContentName = (TextView) convertView.findViewById(R.id.txt_content_name);
tvContentName.setText(dto.contentName);
final int p = position;
final ToggleButton cbDownload = (ToggleButton) convertView.findViewById(R.id.chk_download);
if(dto.checkable){
cbDownload.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked) {
listItem.get(p).checked = true;
if (getCheckedCount() == getCheckableCount()) {
allCheck = true;
listener.onAllCheckChanged(true);
}
} else {
if (allCheck) {
allCheck = false;
listener.onAllCheckChanged(false);
}
listItem.get(p).checked = false;
}
}
});
}
if (dto.checkable) {
cbDownload.setVisibility(View.VISIBLE);
cbDownload.setChecked(dto.checked);
} else {
cbDownload.setVisibility(View.GONE);
cbDownload.setChecked(false);
dto.checked = false;
}
Button btnDownload = (Button)convertView.findViewById(R.id.btn_download_status);
//noinspection VariableNotUsedInsideIf
if (dto.contentStatus != null) {
makeContentStatusText(btnDownload, dto);
makeContentStatus(listItem.get(position).contentId, btnDownload, dto, cbDownload);
}
return convertView;
}
/**
* コンテンツリストを全てチェックの状態にします。<br>
* 全てチェックされている場合はチェックが全て外れます。
*/
public void allCheck() {
for (ContentLinkAdapterDto dto : listItem) {
if (dto.checkable) {
dto.checked = !allCheck;
}
}
}
/**
* textViewをContentStatusの状態に合わせて編集します。
*
* @param btnDownload
* @param dto
*/
private void makeContentStatusText(Button btnDownload, ContentLinkAdapterDto dto) {
String download = context.getString(R.string.download);
switch (dto.contentStatus) {
case IMPOSSIBLE:
btnDownload.setEnabled(false);
btnDownload.setText(download);
break;
case DONE:
btnDownload.setEnabled(true);
btnDownload.setText(context.getString(R.string.open));
break;
case POSSIBLE:
btnDownload.setEnabled(true);
if (dto.selectable) {
btnDownload.setText(download + "/" + context.getString(R.string.streaming));
}
else {
btnDownload.setText(download);
}
break;
case DOWNLOADING:
btnDownload.setEnabled(false);
btnDownload.setText(context.getString(R.string.downloading));
break;
case WAITING:
// もうこのパターンはありえない? Jang
btnDownload.setEnabled(false);
btnDownload.setText(context.getString(R.string.waiting));
break;
case STREAMING:
btnDownload.setEnabled(true);
btnDownload.setText(context.getString(R.string.streaming));
break;
default:
break;
}
}
private void makeContentStatus(final long contentId, final Button btnDownload, final ContentLinkAdapterDto dto, final ToggleButton cbDownload) {
final ContentStatus contentStatus = dto.contentStatus;
btnDownload.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (contentStatus == ContentStatus.POSSIBLE || contentStatus == ContentStatus.WAITING) {
if(!listener.OnDownloadTransfer(contentId, dto)){
// ダウンロードできなかった場合、ステータスを戻す
dto.contentStatus = ContentStatus.POSSIBLE;
dto.checkable = true;
}
ContentDetailLinkAdapter.this.notifyDataSetChanged();
}
else if (contentStatus == ContentStatus.DONE) {
listener.onViewTransfer(contentId);
}
else if (contentStatus == ContentStatus.STREAMING) {
((ABVAuthenticatedActivity)context).startStreamingActivity(contentId);
}
}
});
switch (contentStatus) {
case IMPOSSIBLE:
btnDownload.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null);
cbDownload.setEnabled(false);
break;
case DONE:
btnDownload.setCompoundDrawablesWithIntrinsicBounds(context.getResources().getDrawable(R.drawable.icon_contentdetail_open), null, null, null);
cbDownload.setVisibility(View.INVISIBLE);
cbDownload.setEnabled(true);
break;
case POSSIBLE:
btnDownload.setVisibility(View.VISIBLE);
int resId = dto.selectable? R.drawable.ic_download_streaming_select: R.drawable.icon_contentdetail_download;
btnDownload.setCompoundDrawablesWithIntrinsicBounds(context.getResources().getDrawable(resId), null, null, null);
cbDownload.setEnabled(true);
break;
case DOWNLOADING:
btnDownload.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null);
cbDownload.setEnabled(false);
break;
case WAITING:
btnDownload.setVisibility(View.VISIBLE);
btnDownload.setCompoundDrawablesWithIntrinsicBounds(context.getResources().getDrawable(R.drawable.ic_download_resume), null, null, null);
cbDownload.setEnabled(false);
break;
case STREAMING:
btnDownload.setVisibility(View.VISIBLE);
btnDownload.setCompoundDrawablesWithIntrinsicBounds(context.getResources().getDrawable(R.drawable.icon_contentdetail_streaming), null, null, null);
cbDownload.setEnabled(dto.checkable);
break;
default:
break;
}
}
/**
* チェックされているコンテンツIDを返します。
* @return
*/
public long[] getDownloadList() {
List<ContentLinkAdapterDto> downloadlist = new ArrayList<>();
for (ContentLinkAdapterDto dto :listItem) {
//다운로드 일시정지 중인 콘텐츠는 다운로드 리스트에서 제외(contentLogic.resumeDownload 로 따로 재개)
if (dto.checkable && dto.checked && !dto.contentStatus.equals(ContentLinkAdapterDto.ContentStatus.WAITING)) {
downloadlist.add(dto);
}
}
long[] downloadIds = new long[downloadlist.size()];
for (int i = 0; i < downloadIds.length; i++) {
downloadIds[i] = downloadlist.get(i).contentId;
}
return downloadIds;
}
/**
* チェックできるコンテンツの個数を返します。
* @return
*/
public int getCheckableCount() {
int count = 0;
for (ContentLinkAdapterDto dto :listItem) {
if (dto.checkable) {
count++;
}
}
return count;
}
//2012 12 17 Contents Link else if문 제거
/**
* チェックされているコンテンツの個数を返します。
* @return
*/
public int getCheckedCount() {
int count = 0;
for (ContentLinkAdapterDto dto :listItem) {
if (dto.checkable && dto.checked) {
count++;
}
}
return count;
}
//다운로드 일시 정지중인 콘텐츠 수 체크
public int getDownPauseCheckedCount() {
int count = 0;
for (ContentLinkAdapterDto dto :listItem) {
if (dto.contentStatus.equals(ContentLinkAdapterDto.ContentStatus.WAITING)) {
count++;
}
}
return count;
}
//다운로드 일시 정지중인 콘텐츠 아이디 체크
public List<ContentLinkAdapterDto> getPausedList() {
List<ContentLinkAdapterDto> downloadlist = new ArrayList<>();
for (ContentLinkAdapterDto dto :listItem) {
if (dto.contentStatus.equals(ContentLinkAdapterDto.ContentStatus.WAITING)) {
downloadlist.add(dto);
}
}
return downloadlist;
}
}
package jp.agentec.abook.abv.ui.home.adapter;
import java.util.ArrayList;
import jp.agentec.abook.abv.cl.util.BitmapUtil;
import jp.agentec.abook.abv.launcher.android.R;
import jp.agentec.abook.abv.ui.common.appinfo.AppColor;
import jp.agentec.abook.abv.ui.common.dto.ContentPageSerializableDto;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.Color;
import android.graphics.drawable.GradientDrawable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
/** 
* 詳細リストデータ
* @author Minhyuk Seok
* @version 1.0.0
*/
public class ContentDetailListAdapter extends BaseAdapter {
Context context;
LayoutInflater Inflater;
ArrayList<ContentPageSerializableDto> listItem;
private final int mThumbnailSize;
int layout;
public ContentDetailListAdapter(Context context, int alayout, ArrayList<ContentPageSerializableDto> detailPage) {
this.context = context;
Inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
this.listItem = detailPage;
layout = alayout;
mThumbnailSize = context.getResources().getDimensionPixelSize(R.dimen.content_thumbnail_image_size);
}
@Override
public int getCount() {
if (listItem == null) {
return 0;
}
else
{
return listItem.size();
}
}
@Override
public long getItemId(int position) {
return position;
}
public void setItemId(int position) {
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = Inflater.inflate(layout, parent, false);
}
// setStripeColor(context, convertView, position);
ImageView image = (ImageView)convertView.findViewById(R.id.img_content_detail_list);
final Bitmap resized = BitmapUtil.getResizedBitmap(listItem.get(position).getPageThumbnailPath(), mThumbnailSize, mThumbnailSize, Config.RGB_565);
image.setImageBitmap(resized);
image.setAdjustViewBounds(true);
image.setScaleType(ImageView.ScaleType.FIT_CENTER);
image.setPadding(0, 0, 1, 1);
GradientDrawable g = new GradientDrawable(
GradientDrawable.Orientation.TL_BR,
new int[] {Color.WHITE,Color.WHITE, AppColor.getBackground()});
//noinspection deprecation(API16から非推奨になった。無視)
image.setBackgroundDrawable(g);
TextView comment = (TextView)convertView.findViewById(R.id.txt_content_detail_list_comment);
comment.setText(listItem.get(position).getPageText());
return convertView;
}
@Override
public Object getItem(int position) {
return null;
}
}
\ No newline at end of file
package jp.agentec.abook.abv.ui.home.adapter;
import java.util.List;
import jp.agentec.abook.abv.bl.common.Constant.ReaderConstant.ReceivePushMessage;
import jp.agentec.abook.abv.bl.dto.ContractDto;
import jp.agentec.abook.abv.launcher.android.R;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.CompoundButton;
import android.widget.Switch;
import android.widget.TextView;
public class PushMessageContractListAdapter extends BaseAdapter {
private Context context;
private LayoutInflater inflater;
public List<ContractDto> listItem;
public PushMessageContractListAdapter(Context context, List<ContractDto> listItem) {
this.context = context;
this.listItem = listItem;
inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
for(ContractDto contractDto : listItem){
if(ReceivePushMessage.NOT_SET == contractDto.receivePushMessage){
contractDto.receivePushMessage = ReceivePushMessage.REFUSE;
}
}
}
@Override
public int getCount() {
return listItem.size();
}
@Override
public Object getItem(int position) {
return listItem.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (null == convertView) {
convertView = inflater.inflate(R.layout.item_push_message_contract, null);
holder = new ViewHolder();
holder.tvContractName = (TextView) convertView.findViewById(R.id.tv_contract_name);
holder.swContract = (Switch) convertView.findViewById(R.id.sw_contract);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
final ContractDto contractDto = listItem.get(position);
holder.tvContractName.setText(contractDto.companyName + "[" + contractDto.siteId + "-" + contractDto.contractId + "]");
holder.swContract.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if(isChecked){
contractDto.receivePushMessage = ReceivePushMessage.PERMIT;
} else {
contractDto.receivePushMessage = ReceivePushMessage.REFUSE;
}
}
});
if(ReceivePushMessage.PERMIT == contractDto.receivePushMessage){
holder.swContract.setChecked(true);
} else {
holder.swContract.setChecked(false);
}
return convertView;
}
private static class ViewHolder {
TextView tvContractName;
Switch swContract;
}
}
package jp.agentec.abook.abv.ui.home.dialog;
import jp.agentec.abook.abv.launcher.android.R;
import jp.agentec.abook.abv.ui.common.view.ABVDialog;
import android.annotation.SuppressLint;
import android.content.Context;
import android.view.View;
import android.view.Window;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.widget.TextView;
public class HtmlViewDialog extends ABVDialog {
public HtmlViewDialog(Context context, String linkUrl) {
super(context, android.R.style.Theme_Holo_Light_Dialog_NoActionBar);
init(linkUrl);
}
@SuppressLint("SetJavaScriptEnabled")
private void init(String linkUrl) {
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.dialog_htmlview);
setCanceledOnTouchOutside(false);
findViewById(R.id.close_dialog).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
dismiss();
}
});
WebView webView = (WebView) findViewById(R.id.htmlWebView);
webView.loadUrl(linkUrl);
// //ブラウザの描画領域を対象としたイベントをフック
WebSettings settings = webView.getSettings();
settings.setSupportMultipleWindows(true); // 新しいウィンドウを開くイベントを取得する
settings.setLoadsImagesAutomatically(true); // イメージを自動的にロードする
settings.setBuiltInZoomControls(true); // ズーム機能を有効にする
settings.setSupportZoom(true); // ズーム機能を有効にする
settings.setJavaScriptEnabled(true); // JavaScriptを有効にする
settings.setLoadWithOverviewMode(true); // 画面の横幅にページの横幅を合わせる
settings.setUseWideViewPort(true); // 画面の横幅にページの横幅を合わせる
}
public void setToolbarTitle(String title) {
TextView titleView = (TextView) findViewById(R.id.dialog_title);
titleView.setText(title);
}
}
package jp.agentec.abook.abv.ui.home.dialog;
import android.app.Activity;
import android.view.View;
import android.view.Window;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import java.util.List;
import jp.agentec.abook.abv.bl.common.CommonExecutor;
import jp.agentec.abook.abv.bl.common.exception.AcmsException;
import jp.agentec.abook.abv.bl.common.exception.NetworkDisconnectedException;
import jp.agentec.abook.abv.bl.common.log.Logger;
import jp.agentec.abook.abv.bl.data.dao.AbstractDao;
import jp.agentec.abook.abv.bl.data.dao.ContractDao;
import jp.agentec.abook.abv.bl.dto.ContractDto;
import jp.agentec.abook.abv.bl.logic.AbstractLogic;
import jp.agentec.abook.abv.bl.logic.ContractLogic;
import jp.agentec.abook.abv.cl.push.FcmManager;
import jp.agentec.abook.abv.launcher.android.R;
import jp.agentec.abook.abv.ui.common.constant.ErrorMessage;
import jp.agentec.abook.abv.ui.common.helper.ProgressDialogHelper;
import jp.agentec.abook.abv.ui.common.util.ABVToastUtil;
import jp.agentec.abook.abv.ui.common.view.ABVDialog;
import jp.agentec.abook.abv.ui.home.adapter.PushMessageContractListAdapter;
/**
* Readerアプリへコンテンツを共有する際に設定する情報の画面
* @author jang
*
*/
public class PushMessageSettingDialog extends ABVDialog {
private Activity activity;
private ContractLogic logic;
public PushMessageSettingDialog(Activity activity) {
super(activity, android.R.style.Theme_Holo_Light_Dialog_NoActionBar);
this.activity = activity;
logic = AbstractLogic.getLogic(ContractLogic.class);
init();
}
private void init() {
ContractDao contractDao = AbstractDao.getDao(ContractDao.class);
List<ContractDto> contractList = contractDao.getListByUsablePushMessage();
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.push_message_setting);
setCanceledOnTouchOutside(false);
ListView lvContract = (ListView) findViewById(R.id.list_contract);
// キャンセルボタン
findViewById(R.id.cancel).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
dismiss();
}
});
if(contractList.size() > 0){
final PushMessageContractListAdapter adapter = new PushMessageContractListAdapter(getContext(), contractList);
lvContract.setAdapter(adapter);
findViewById(R.id.ok).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ProgressDialogHelper.showProgressPopup(activity, true);
final String fcmToken = FcmManager.getFcmToken(getContext());
if (fcmToken == null || fcmToken.equals("noneId")) {
ABVToastUtil.showMakeText(getContext(), R.string.fcm_not_supported, Toast.LENGTH_SHORT);
} else {
CommonExecutor.execute(new Runnable() {
@Override
public void run() {
try {
logic.updateReceivePushMessage(getContext().getString(R.string.repository_fqdn), fcmToken, adapter.listItem);
dismiss();
} catch (AcmsException e) {
ErrorMessage.showErrorMessageToast(getContext(), e);
Logger.e("PushMessageSettingDialog", e);
} catch (NetworkDisconnectedException e) {
ABVToastUtil.showMakeText(getContext(), R.string.request_network_connection, Toast.LENGTH_SHORT);
} finally {
ProgressDialogHelper.closeProgressPopup();
}
}
});
}
}
});
} else {
((TextView) findViewById(R.id.tv_summary)).setText(getContext().getString(R.string.push_message_contract_not_exist));
findViewById(R.id.ok).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
dismiss();
}
});
}
}
}
package jp.agentec.abook.abv.ui.home.dto;
import java.util.Date;
public class ContentLinkAdapterDto {
public String thumbnailPath;
public String contentName;
public long contentId;
public ContentStatus contentStatus;
public boolean checked = false;
public boolean checkable = false;
public boolean selectable = false;
public Date deliveryStartDate;
public enum ContentStatus {
IMPOSSIBLE, DONE, POSSIBLE, DOWNLOADING, WAITING, STREAMING
}
}
......@@ -83,7 +83,6 @@ import jp.agentec.abook.abv.ui.common.dto.ContentPageSerializableDto;
import jp.agentec.abook.abv.ui.common.util.ABVToastUtil;
import jp.agentec.abook.abv.ui.common.util.AlertDialogUtil;
import jp.agentec.abook.abv.ui.common.vo.ContentMyList;
import jp.agentec.abook.abv.ui.home.activity.ContentDetailViewActivity;
import jp.agentec.abook.abv.ui.home.activity.ProjectListActivity;
import jp.agentec.abook.abv.ui.home.activity.ProjectMeetingListActivity;
import jp.agentec.abook.abv.ui.home.activity.ProjectRelatedContentActivity;
......@@ -94,7 +93,6 @@ import jp.agentec.abook.abv.ui.viewer.activity.EnqueteWebViewActivity;
import jp.agentec.abook.abv.ui.viewer.activity.HTMLWebViewActivity;
import jp.agentec.abook.abv.ui.viewer.activity.HTMLXWalkWebViewActivity;
import jp.agentec.abook.abv.ui.viewer.activity.ImageViewActivity;
import jp.agentec.abook.abv.ui.viewer.activity.PanoViewActivity;
import jp.agentec.abook.abv.ui.viewer.activity.ParentWebViewActivity;
import jp.agentec.abook.abv.ui.viewer.activity.PreviewActivity;
import jp.agentec.abook.abv.ui.viewer.activity.VideoViewActivity;
......@@ -361,139 +359,8 @@ public class ActivityHandlingHelper extends ABookHelper implements RemoteObserve
}
}
public void contentDetailActivityMove(long contentId, int prePage) {
ABVAuthenticatedActivity activity = getCurrentActivity();
contentDetailActivityMove(activity, contentId, activity.getClass().getSimpleName(), prePage, false, null);
}
public void contentDetailActivityMove(final Context context, final long contentId, final String className, final int pre_page, final boolean contentLinkFlag, final Callback callback) {
CommonExecutor.execute(new Runnable() {
@Override
public void run() {
try {
ContentDto contentDto = contentDao.getContent(contentId);
ContentDto detail;
// PDFコンテンツ以外は詳細を取得しにいかない
if (contentDto.contentType == null || ContentJSON.KEY_PDF_TYPE.equals(contentDto.contentType.toLowerCase(Locale.US))) {
detail = contentLogic.getContentDetail(contentId);
} else {
detail = contentDto;
}
if (detail != null) {
callContentDetailActivity(context, detail, className, pre_page, contentLinkFlag);
if (callback != null) {
callback.callback(null);
}
} else {
contentDownloader.downloadContentDetail(contentId);
}
} catch (NetworkDisconnectedException e) {
if (callback != null) {
callback.callback(ErrorCode.NETWORK);
}
} catch (Exception e) {
Logger.e(TAG, "Exception " + e);
if (callback != null) {
callback.callback(ErrorCode.E107);
}
}
}
});
}
// ActivityView에서 사용할수 있기 위하여 callContentDetailActivity 추가
// 상세화면에서 이전화면 이동시 Bug수정
private void callContentDetailActivity(Context context, final ContentDto detail, final String className, int pre_page, boolean contentLinkFlag) {
Intent intent = new Intent();
intent.setClassName(context.getPackageName(), ContentDetailViewActivity.class.getName());
if (contentLinkFlag) {
intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
}
if (context instanceof PanoViewActivity) {
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
}
String dateFormat = "yyyy'" + context.getResources().getString(R.string.date_format_year) + "'MM'" + context.getResources().getString(R.string.date_format_month) + "'dd'"
+ context.getResources().getString(R.string.date_format_day) + "'HH:mm:ss";
intent.putExtra(ABookKeys.CONTENT_ID, detail.contentId);
intent.putExtra("ThumbnailNormalPath", detail.thumbnailNormalPath);
intent.putExtra("ContentName", detail.contentName);
intent.putExtra("ContentDetail", detail.contentDetail);
intent.putExtra("DeliveryStartDate", DateTimeUtil.toString(detail.deliveryStartDate, dateFormat));
intent.putExtra("DeliveryEndDate", DateTimeUtil.toString(detail.deliveryEndDate, dateFormat));
intent.putExtra("ContentSize", detail.contentSize);
intent.putExtra("LastDeliveryDate", DateTimeUtil.toString(detail.lastDeliveryDate, dateFormat));
intent.putExtra("ContentType", detail.contentType);
intent.putExtra("ContractContentId", detail.contractContentId);
// 콘텐츠 정보 (콘텐츠 경고 ?)
intent.putExtra("ContentAlertLevel", detail.alertMessageLevel);
intent.putExtra("AllPageNum", detail.allPageNum);
intent.putExtra("DownloadedFlg", detail.downloadedFlg);
intent.putExtra("DownloadingFlg", detail.downloadingFlg);
intent.putExtra("UpdatedFlg", detail.updatedFlg);
intent.putExtra("FromClassName", className);
boolean pdfSendMailFlg = false;
ContractLogic contractLgic = AbstractLogic.getLogic(ContractLogic.class);
if (contractLgic.getPdfSendMail() && detail.pdfSendMailFlg && detail.contentType.equals(ContentJSON.KEY_PDF_TYPE)) {
pdfSendMailFlg = true;
}
intent.putExtra("PdfSendMailFlg", pdfSendMailFlg);
intent.putExtra("PrintFlg", detail.printFlg);
intent.putExtra("ReaderShareFlg", detail.readerShareFlg);
intent.putExtra("HideFlg", detail.hideFlg);
DownloadStatusType type = detail.getDownloadStatus();
if (type == DownloadStatusType.Paused) {
intent.putExtra("PausedFlg", true);
} else {
intent.putExtra("PausedFlg", false);
}
if (pre_page != -1) {
intent.putExtra("pre_page", pre_page);
}
intent.putExtra("ContentLinkFlag", contentLinkFlag);
if (ContentMyList.searchDivisionType != null) {
intent.putExtra("seartSelect", ContentMyList.searchDivisionType.type());
intent.putExtra("seartComment", ContentMyList.searchKeyword);
}
ArrayList<ContentPageSerializableDto> spages = new ArrayList<>();
if (detail.pages != null) {
for (int i = 0; i < detail.pages.size() && i < 6; i++) {
ContentPageSerializableDto pagedata = new ContentPageSerializableDto();
pagedata.setPageText(detail.pages.get(i).pageText);
pagedata.setPageThumbnailPath(detail.pages.get(i).getPageThumbnailPath());
spages.add(pagedata);
}
}
intent.putExtra("Pages", spages);
// 그룹, 장르 정보
long contentId = detail.contentId;
ArrayList<String> groupList;
ArrayList<String> categoryList;
groupList = AbstractLogic.getLogic(GroupLogic.class).getExistContentsGroupInfo(contentId);
categoryList = AbstractLogic.getLogic(CategoryLogic.class).getExistContentsCategoryInfo(contentId);
intent.putStringArrayListExtra("GroupInfo", groupList);
intent.putStringArrayListExtra("CategoryInfo", categoryList);
if (context instanceof PanoViewActivity) {
((Activity)context).finish();
}
context.startActivity(intent);
}
private void startActivity(Context context, Intent intent, NaviConsts ABVNavi, Integer flags, long contentId, String contentType, String path) throws Exception {
contentRefresher.stopRefresh(); // 新着更新を停止する
// 360°用のWebサーバが動作している場合停止
PanoViewHelper.getInstance().callStopPanoServer();
if (intent == null) {
intent = new Intent();
}
......@@ -514,11 +381,6 @@ public class ActivityHandlingHelper extends ABookHelper implements RemoteObserve
intent.setClass(context, ImageViewActivity.class);
} else if (contentType.equals(ContentJSON.KEY_MOVIE_TYPE)) { // 動画ファイル
intent.setClass(context, VideoViewActivity.class);
} else if (contentType.equals(ContentJSON.KEY_PANO_MOVIE_TYPE) && Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { // 360°動画(5.0未満)
intent.setClass(context, PanoViewActivity.class);
intent.putExtra("path", path);
intent.putExtra("contentId", contentId);
context.startActivity(intent);
} else if (contentType.equals(ContentJSON.KEY_MUSIC_TYPE)) { // 音声ファイル
intent.setClass(context, AudioPlayActivity.class);
} else if (contentType.equals(ContentJSON.KEY_HTML_TYPE)) { // HTMLファイル
......@@ -1365,7 +1227,8 @@ public class ActivityHandlingHelper extends ABookHelper implements RemoteObserve
} else {
handler.post(new Runnable() {
@Override
public void run() {showDownloadConfirm(contentId, contentDto.contentName);
public void run() {
showDownloadConfirm(contentId, contentDto.contentName);
}
});
}
......
package jp.agentec.abook.abv.ui.home.helper;
import android.app.Activity;
import android.content.Context;
import android.widget.Toast;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import fi.iki.elonen.NanoHTTPD;
import jp.agentec.abook.abv.bl.common.ABVEnvironment;
import jp.agentec.abook.abv.bl.common.Callback;
import jp.agentec.abook.abv.bl.common.Constant;
import jp.agentec.abook.abv.bl.common.log.Logger;
import jp.agentec.abook.abv.bl.data.dao.AbstractDao;
import jp.agentec.abook.abv.bl.data.dao.ContentDao;
import jp.agentec.abook.abv.bl.dto.ContentDto;
import jp.agentec.abook.abv.bl.logic.AbstractLogic;
import jp.agentec.abook.abv.bl.logic.ContentObjectLogLogic;
import jp.agentec.abook.abv.bl.logic.ContentReadingLogLogic;
import jp.agentec.abook.abv.bl.net.PanoServer;
import jp.agentec.abook.abv.cl.environment.NetworkAdapter;
import jp.agentec.abook.abv.cl.util.ContentLogUtil;
import jp.agentec.abook.abv.cl.util.PreferenceUtil;
import jp.agentec.abook.abv.launcher.android.R;
import jp.agentec.abook.abv.ui.common.activity.ABVContentViewActivity;
import jp.agentec.abook.abv.ui.common.appinfo.AppDefType;
import jp.agentec.abook.abv.ui.common.util.ABVToastUtil;
import jp.agentec.abook.abv.ui.viewer.activity.HTMLWebViewActivity;
import jp.agentec.adf.util.DateTimeFormat;
import jp.agentec.adf.util.DateTimeUtil;
import jp.agentec.adf.util.FileUtil;
/**
* Created by jang on 2016/03/07.
*/
public class PanoViewHelper extends ABookHelper {
private static final String TAG = PanoViewHelper.class.getSimpleName();
private static PanoViewHelper panoViewHelper = null;
private List<PanoServer> panoServerList = new ArrayList<>();
public static synchronized PanoViewHelper getInstance() {
if (panoViewHelper == null) {
panoViewHelper = new PanoViewHelper();
}
return panoViewHelper;
}
public void startPanoContent(Context context, String path, long contentId) {
// ローカルIPアドレス取得
String ipAddress = NetworkAdapter.getInstance().getLocalIpAddress();
if (ipAddress == null) {
Logger.e(TAG, "unable to get local ip. Wi-Fi Network Not Available.");
ABVToastUtil.showMakeText(context, R.string.C_E_SYSTEM_0004, Toast.LENGTH_LONG);
return;
}
// Webサーバ開始
int port = context.getResources().getInteger(R.integer.pano_web_server_port);
boolean isStart = false;
for (int i = 0; i < 20; i++) { // 最大20までポート番号を増やす
if (startPanoServer(context, ipAddress, port, contentId, path)) {
isStart = true;
break;
}
port++;
}
if (!isStart) { // 10回行ってもすべてPortが埋まっている場合はエラーとする
ABVToastUtil.showMakeText(context, R.string.C_E_SYSTEM_0001, Toast.LENGTH_LONG);
return;
}
// Chromeを起動し、起動したWebServerのアドレスを渡す。
startChrome(context, "http://" + ipAddress + ":" + port + "/" + contentId + "/?app=android");
}
private boolean startPanoServer(final Context context, String ipAddress, int port, final Long contentId, String path) {
final PanoServer panoServer = new PanoServer(ipAddress, port, contentId, FileUtil.getParentPath(path) + "/", !Logger.isDebugEnabled(), false, Arrays.asList("Android", "Chrome"), new Callback() {
int lastPageNo = 0;
Date lastPageStartDate = null;
Integer readingLogId = null;
@Override
public Object callback(Object ret) {
if (ret instanceof Map) {
Map<String, String> param = (Map<String, String>) ret;
return doParameter(param, context, contentId, lastPageNo, readingLogId, lastPageStartDate, new Callback() {
@Override
public Object callback(Object ret) {
Map<String, Object> map = (Map<String, Object>) ret;
if (map.containsKey("lastPageNo")) {
lastPageNo = (int) map.get("lastPageNo");
}
if (map.containsKey("lastPageStartDate")) {
lastPageStartDate = (Date) map.get("lastPageStartDate");
}
if (map.containsKey("readingLogId")) {
readingLogId = (Integer) map.get("readingLogId");
}
return null;
}
});
} else if (ret instanceof Long) { // onStop
Long contentId = (Long) ret; // srcContentIdと同じはずなので使わない
ContentLogUtil.getInstance().endContentPageReadLog(contentId, lastPageNo, readingLogId);
AbstractLogic.getLogic(ContentReadingLogLogic.class).endContentReadLog(contentId);
}
return null;
}
});
try {
panoServer.start(NanoHTTPD.SOCKET_READ_TIMEOUT, false); // メインスレッド終了後も起動し続ける場合false
addPanoServerList(panoServer);
return true;
} catch (IOException e) {
Logger.e(TAG, "The server could not start.", e);
stopPanoServer(panoServer);
return false;
}
}
public String doParameter(Map<String, String> param, Context context, long contentId, Integer lastPageNo, Integer readingLogId, Date lastPageStartDate, Callback callback) {
String uri = param.get(PanoServer.URI);
Long dstContentId = Long.valueOf(param.get("contentId"));
Integer pageNo = param.containsKey("pageNo")? Integer.valueOf(param.get("pageNo")) - 1: 0; // 1スタートを0スタート
if (uri.startsWith(PanoServer.CONTENTLINK)) {
int result = ContentViewHelper.getInstance().linkDownloadChecked(dstContentId, pageNo);
Logger.d(TAG, "linkDownloadChecked result=%s", result);
if (result == ContentViewHelper.LinkContentStatus.NoExist) {
return context.getString(R.string.msg_require_content_refresh);
}
ContentLogUtil.getInstance().endContentPageReadLog(contentId, lastPageNo, readingLogId);
if (result == ContentViewHelper.LinkContentStatus.DownloadedContent) {
Activity currentActivity = ActivityHandlingHelper.getInstance().getContentViewActivity();
// HTMLWebViewActivityの場合はfinish()を実施して、コンテンツを開く
if (currentActivity instanceof HTMLWebViewActivity) {
currentActivity.finish();
}
ActivityHandlingHelper.getInstance().startContentActivity(dstContentId, pageNo);
} else {
int prePage = 0;
ABVContentViewActivity viewActivity = ActivityHandlingHelper.getInstance().getContentViewActivity();
if (viewActivity != null) {
prePage = viewActivity.getCurrentPageNumber();
}
ActivityHandlingHelper.getInstance().contentDetailActivityMove(dstContentId, prePage);
ContentDto contentDto = AbstractDao.getDao(ContentDao.class).getContent(dstContentId);
return PanoServer.OK + "." + String.format(context.getString(R.string.msg_require_content_download), (contentDto==null?"":contentDto.contentName));
}
} else if (uri.startsWith(PanoServer.DETAILLOG)) {
if (readingLogId == null) { // 開始
readingLogId = ContentLogUtil.getInstance().startContentReadLog(context, contentId, PreferenceUtil.getUserPref(context, AppDefType.DefPrefKey.PERMISSION_ACCESS_LOCATION, false));
}
AbstractLogic.getLogic(ContentObjectLogLogic.class).insertContentObjectLog(contentId, readingLogId, pageNo, param);
} else if (uri.startsWith(PanoServer.MOVEPAGE)) { // ブラウザから送られてきた時間は使わず、アプリの時間を用いる
if (readingLogId == null) { // 開始ページ
readingLogId = ContentLogUtil.getInstance().startContentReadLog(context, contentId, PreferenceUtil.getUserPref(context, AppDefType.DefPrefKey.PERMISSION_ACCESS_LOCATION, false));
ContentLogUtil.getInstance().startContentPageReadLog(contentId, readingLogId, pageNo);
} else {
if (param.get("readingEndDate") != null) { // 終了時刻有
ContentLogUtil.getInstance().endContentPageReadLog(contentId, pageNo, readingLogId);
} else {
ContentLogUtil.getInstance().endContentPageReadLog(contentId, lastPageNo, readingLogId); // crosswalkのときもjsではローカルサーバに送るが受取がないためここで終了を呼び出す。ただしローカルサーバを使う場合2重に処理が走る。
ContentLogUtil.getInstance().startContentPageReadLog(contentId, readingLogId, pageNo);
}
}
String[] readingStartDate = param.get("readingStartDate").split(",");
Date pageStartDate = DateTimeUtil.toDate(readingStartDate[0], readingStartDate[1], DateTimeFormat.yyyyMMddHHmmss_hyphen);
if (lastPageStartDate == null || lastPageStartDate.before(pageStartDate)) {
lastPageStartDate = pageStartDate;
lastPageNo = pageNo;
}
}
Map<String, Object> ret = new HashMap<String, Object>();
if (lastPageStartDate != null) {
ret.put("lastPageStartDate", lastPageStartDate);
}
if (lastPageNo != null) {
ret.put("lastPageNo", lastPageNo);
}
if (readingLogId != null) {
ret.put("readingLogId", readingLogId);
}
callback.callback(ret);
return null;
}
private void addPanoServerList(PanoServer addPanoServer) {
for (int i = panoServerList.size() - 1; i >= 0; i--) { // 終了したものがあればListから除去
PanoServer panoServer = panoServerList.get(i);
if (!panoServer.isAlive()) {
panoServerList.remove(i);
}
}
panoServerList.add(addPanoServer);
}
private void stopPanoServer(PanoServer panoServer) {
if (panoServer != null) {
Logger.i(TAG, "stop pano server.");
panoServer.stop();
panoServerList.remove(panoServer);
}
}
public void callStopPanoServer() {
for (PanoServer panoServer : panoServerList) {
panoServer.callStop();
}
}
public void stopAllPanoServer() {
Logger.i(TAG, "stop all pano server.");
for (int i = panoServerList.size() - 1; i >= 0; i--) {
PanoServer panoServer = panoServerList.get(i);
panoServer.stop();
panoServerList.remove(i);
}
}
public boolean hasAliveServer() {
if (panoServerList.isEmpty()) {
return false;
}
for (PanoServer panoServer : panoServerList) {
if (!panoServer.isCloseWaiting()) {
return true;
}
}
return false;
}
}
package jp.agentec.abook.abv.ui.home.view;
import jp.agentec.abook.abv.launcher.android.R;
import android.content.Context;
import android.graphics.Color;
import android.widget.Button;
/**
* パンくず履歴用のボタン
* @author jang
*
*/
public class BreadCrumbButton extends Button {
private BreadCrumbButton(Context context) {
super(context);
}
public BreadCrumbButton(Context context, String name) {
super(context);
this.name = name;
setText(name);
setTextColor(Color.BLACK);
setBackgroundResource(R.drawable.btn_breadcrumbs);
setPadding(10, 0, 40, 0);
}
public int level;
public long dtoId;
public String name;
}
package jp.agentec.abook.abv.ui.home.view;
import android.content.Context;
import android.os.Bundle;
import android.preference.ListPreference;
import android.util.AttributeSet;
/**
* Created by ThanhChung on 2016/06/02.
*/
public class CustomListPreference extends ListPreference {
private Context mContext;
private boolean isHideDialog;
public CustomListPreference(Context context) {
super(context);
mContext = context;
}
public CustomListPreference(Context context, AttributeSet attrs) {
super(context, attrs);
mContext = context;
}
public void setHideDialog(boolean isHideDialog) {
this.isHideDialog = isHideDialog;
}
@Override
protected void showDialog(Bundle state) {
if (!isHideDialog) {
super.showDialog(state);
}
}
@Override
protected void onDialogClosed(boolean positiveResult) {
super.onDialogClosed(positiveResult);
}
}
......@@ -5003,94 +5003,6 @@ public class ContentViewActivity extends ABVContentViewActivity {
}
//2012 12 17 Contents link by jeonghun
private void linkDownloadChecked(final long contentId, int page) {
Logger.i(TAG, "linkDownloadChecked targetContentId=" + contentId + " targetPage=" + page);
int result = ContentViewHelper.getInstance().linkDownloadChecked(contentId, page);
switch (result) {
case LinkContentStatus.FolderLock:
{
// フォルダーロックしている状態でかつリンク先のコンテンツが別のフォルダーにある
ABookAlertDialog linkLockDialog = AlertDialogUtil.createAlertDialog(this, R.string.content_link);
linkLockDialog.setMessage(R.string.link_lock_folder);
linkLockDialog.setPositiveButton(R.string.confirm, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int whichButton) {
dialog.dismiss();
}
});
showAlertDialog(linkLockDialog);
}
break;
case LinkContentStatus.NoExist:
{
// コンテンツが存在しない
ABookAlertDialog linknonServerDialog = AlertDialogUtil.createAlertDialog(this, R.string.content_link);
linknonServerDialog.setMessage(R.string.link_new_nonexits);
linknonServerDialog.setPositiveButton(R.string.confirm, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int whichButton) {
dialog.dismiss();
}
});
showAlertDialog(linknonServerDialog);
}
break;
case LinkContentStatus.BeforeReleaseDate:
{
// 公開日前
ABookAlertDialog linknonServerDialog = AlertDialogUtil.createAlertDialog(this, R.string.content_link);
linknonServerDialog.setMessage(R.string.reader_content_download_404);
linknonServerDialog.setPositiveButton(R.string.confirm, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int whichButton) {
dialog.dismiss();
}
});
showAlertDialog(linknonServerDialog);
}
break;
case LinkContentStatus.DownloadedContent:
{
// コンテンツがダウンロード済み
mActivityFinishFlg = true;
allReset();
if (isEnquete) {
isEnquete = false;
mMainLayout.removeView(enqueteLayout);
}
//戻る用のコンテンツIDをセット
getABVUIDataCache().setReturnContentIdList(getContentId(), getCurrentPageNumber());
Intent intent = new Intent();
intent.putExtra("page", page - 1);
Logger.d(TAG, "next page : %s", (page - 1));
if (mProjectId != null) {
intent.putExtra(ABookKeys.PROJECT_ID, mProjectId);
}
startContentViewActivity(intent, contentId);
}
break;
case LinkContentStatus.StreamingContent:
{
Intent intent = new Intent();
intent.putExtra("page", page - 1);
startStreamingActivity(contentId, intent);
}
break;
case LinkContentStatus.ShowDetailContent:
{
// 未ダウンロードの場合詳細画面アラートを表示
showDetailOpenAlert();
}
break;
}
}
private boolean checkValidContentLink(long contentId) {
ContentDto content = AbstractDao.getDao(ContentDao.class).getContent(contentId);
if (content == null) {
......@@ -5100,39 +5012,9 @@ public class ContentViewActivity extends ABVContentViewActivity {
return ContentJSON.KEY_PDF_TYPE.equals(content.contentType) || ContentJSON.KEY_NONE_TYPE.equals(content.contentType);
}
private void showDetailOpenAlert() {
//해당 콘텐츠를 다운받기위한 AlertDialog 호출
ABookAlertDialog linkDialog = AlertDialogUtil.createAlertDialog(ContentViewActivity.this, getString(R.string.confirm), getString(R.string.link_non_download));
linkDialog.setNegativeButton(R.string.no, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
});
//해당 콘텐츠가 없는 경우 다운 받기 위하여 Detail 화면으로 이동하여 콘텐츠 다운로드
linkDialog.setPositiveButton(R.string.confirm, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
contentLinkStatus = true;
//jeonghun 콘텐츠 링크 실행시 앙케이트 닫아 버린다.
if (isEnquete) {
isEnquete = false;
mMainLayout.removeView(enqueteLayout);
}
contentDetailActivityMove(getContentId(), ContentViewActivity.class.getName(), mCurrentPageNumber, contentLinkStatus);
}
});
showAlertDialog(linkDialog);
}
/**
* 콘텐츠 링크 중 콘텐츠가 없을 경우 콘텐츠 다운을 받기위하여
* contentDetailActivityMove 를 다시 한번 호출
* コンテンツリンク中でコンテンツがない場合、ダウンロードするように
* checkContentをもう一度呼び出す
*
*/
......@@ -5154,7 +5036,7 @@ public class ContentViewActivity extends ABVContentViewActivity {
}
});
} else {
contentDetailActivityMove(getContentId(), ContentViewActivity.class.getName(), mCurrentPageNumber , contentLinkStatus);
ActivityHandlingHelper.getInstance().checkContent(getContentId(), mCurrentPageNumber);
}
}
......
......@@ -52,7 +52,6 @@ import jp.agentec.abook.abv.bl.dto.ContentDto;
import jp.agentec.abook.abv.bl.logic.AbstractLogic;
import jp.agentec.abook.abv.bl.logic.ContentCustomLogLogic;
import jp.agentec.abook.abv.bl.logic.ContractLogic;
import jp.agentec.abook.abv.bl.net.PanoServer;
import jp.agentec.abook.abv.cl.util.ContentLogUtil;
import jp.agentec.abook.abv.cl.util.LocationManagerUtil;
import jp.agentec.abook.abv.cl.util.PreferenceUtil;
......@@ -382,7 +381,7 @@ public class HTMLWebViewActivity extends ParentWebViewActivity {
} else {
// AndroidOSが5以下のPANO_SERVER処理のため、置き換える必要がある。
url = "/" + url;
if (url.startsWith(PanoServer.SEND_CUSTOM_LOG)) {
if (url.startsWith(ABookKeys.SEND_CUSTOM_LOG)) {
CommonExecutor.execute(new Runnable() {
@Override
public void run() {
......@@ -403,7 +402,6 @@ public class HTMLWebViewActivity extends ParentWebViewActivity {
commonShouldOverrideUrlLoading(uri, null);
}
}
return true;
}
......@@ -757,24 +755,21 @@ public class HTMLWebViewActivity extends ParentWebViewActivity {
pauseBtn.setVisibility(View.GONE);
exitMeetingBtn.setVisibility(View.GONE);
promoteBtn.setVisibility(View.GONE);
}
else {
} else {
subMenuBtn.setVisibility(View.GONE);
mBtnRemoteStart.setVisibility(View.GONE);
pauseBtn.setVisibility(View.VISIBLE);
exitMeetingBtn.setVisibility(View.VISIBLE);
promoteBtn.setVisibility(View.VISIBLE);
}
}
else {
} else {
closeButton.setVisibility(View.VISIBLE);
if (objectId == -1) {
historyListBtn.setVisibility(View.VISIBLE);
if(!(ABVDataCache.getInstance().serviceOption.isABookCheck() && ABVEnvironment.getInstance().isABookCheck())) {
subMenuBtn.setVisibility(View.VISIBLE);
}
}
else {
} else {
historyListBtn.setVisibility(View.GONE);
subMenuBtn.setVisibility(View.GONE);
}
......
......@@ -2,11 +2,8 @@ package jp.agentec.abook.abv.ui.viewer.activity;
import android.content.DialogInterface;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.provider.MediaStore;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
......@@ -30,7 +27,6 @@ import org.xwalk.core.XWalkView;
import org.xwalk.core.XWalkWebResourceRequest;
import org.xwalk.core.XWalkWebResourceResponse;
import java.io.File;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
......@@ -53,23 +49,17 @@ import jp.agentec.abook.abv.bl.logic.AbstractLogic;
import jp.agentec.abook.abv.bl.logic.ContentCustomLogLogic;
import jp.agentec.abook.abv.bl.logic.ContentReadingLogLogic;
import jp.agentec.abook.abv.bl.logic.ContractLogic;
import jp.agentec.abook.abv.bl.logic.ProjectLogic;
import jp.agentec.abook.abv.bl.net.PanoServer;
import jp.agentec.abook.abv.cl.util.ContentLogUtil;
import jp.agentec.abook.abv.cl.util.PreferenceUtil;
import jp.agentec.abook.abv.launcher.android.R;
import jp.agentec.abook.abv.ui.common.appinfo.AppDefType;
import jp.agentec.abook.abv.ui.common.constant.ErrorCode;
import jp.agentec.abook.abv.ui.common.constant.ErrorMessage;
import jp.agentec.abook.abv.ui.common.dialog.ABookAlertDialog;
import jp.agentec.abook.abv.ui.common.util.ABVToastUtil;
import jp.agentec.abook.abv.ui.common.util.AlertDialogUtil;
import jp.agentec.abook.abv.ui.common.view.ABVPopupListWindow;
import jp.agentec.abook.abv.ui.home.helper.ActivityHandlingHelper;
import jp.agentec.abook.abv.ui.home.helper.PanoViewHelper;
import jp.agentec.adf.util.DateTimeFormat;
import jp.agentec.adf.util.DateTimeUtil;
import jp.agentec.adf.util.FileUtil;
/**
* Created by leej on 2018/04/17.
......@@ -341,7 +331,7 @@ public class HTMLXWalkWebViewActivity extends ParentWebViewActivity {
public boolean shouldOverrideUrlLoading(XWalkView view, String url) {
Logger.d(TAG, "shouldOverrideUrlLoading: %s", url);
if (url.startsWith(PanoServer.ABOOK)) {
if (url.startsWith(ABookKeys.ABOOK)) {
final Uri uri = Uri.parse(url);
if (isStreaming) {
if (url.startsWith("abooksendlog://")) { // ログ送信完了
......@@ -368,7 +358,7 @@ public class HTMLXWalkWebViewActivity extends ParentWebViewActivity {
} else {
// AndroidOSが5以下のPANO_SERVER処理のため、置き換える必要がある。
url = "/" + url;
if (url.startsWith(PanoServer.SEND_CUSTOM_LOG)) {
if (url.startsWith(ABookKeys.SEND_CUSTOM_LOG)) {
CommonExecutor.execute(new Runnable() {
@Override
public void run() {
......@@ -386,15 +376,15 @@ public class HTMLXWalkWebViewActivity extends ParentWebViewActivity {
}
});
}
else if (url.startsWith(PanoServer.MOVEPAGE) || url.startsWith(PanoServer.CONTENTLINK) || url.startsWith(PanoServer.DETAILLOG)){
else if (url.startsWith(ABookKeys.MOVEPAGE) || url.startsWith(ABookKeys.CONTENTLINK) || url.startsWith(ABookKeys.DETAILLOG)){
Map<String, String> param = new HashMap<String, String>();
for (String key : uri.getQueryParameterNames()) {
param.put(key, uri.getQueryParameter(key));
}
param.put(PanoServer.URI, url);
param.put(ABookKeys.URI, url);
// プロジェクト関連資料なのかをチェック
if ( url.startsWith(PanoServer.CONTENTLINK) && ABVEnvironment.getInstance().isABookCheck()) {
if ( url.startsWith(ABookKeys.CONTENTLINK) && ABVEnvironment.getInstance().isABookCheck()) {
Long dstContentId = Long.valueOf(param.get("contentId"));
if (ActivityHandlingHelper.getInstance().isExistCommonContent(contentId)) {
if (!ActivityHandlingHelper.getInstance().isExistCommonContent(dstContentId)) {
......@@ -409,33 +399,33 @@ public class HTMLXWalkWebViewActivity extends ParentWebViewActivity {
}
}
String ret = PanoViewHelper.getInstance().doParameter(param, getApplicationContext(), contentId, lastPageNo, readingLogId, lastPageStartDate, new Callback() {
String ret = doParameter(param, getApplicationContext(), contentId, lastPageNo, readingLogId, lastPageStartDate, new Callback() {
@Override
public Object callback(Object ret) {
Map<String, Object> map = (Map<String, Object>) ret;
if (map.containsKey(PanoServer.PARAM_LAST_PAGE_NUMBER)) {
lastPageNo = (int) map.get(PanoServer.PARAM_LAST_PAGE_NUMBER);
if (map.containsKey(ABookKeys.PARAM_LAST_PAGE_NUMBER)) {
lastPageNo = (int) map.get(ABookKeys.PARAM_LAST_PAGE_NUMBER);
}
if (map.containsKey(PanoServer.PARAM_LAST_PAGE_START_DATE)) {
lastPageStartDate = (Date) map.get(PanoServer.PARAM_LAST_PAGE_START_DATE);
if (map.containsKey(ABookKeys.PARAM_LAST_PAGE_START_DATE)) {
lastPageStartDate = (Date) map.get(ABookKeys.PARAM_LAST_PAGE_START_DATE);
}
if (map.containsKey(PanoServer.PARAM_READING_LOG_ID)) {
readingLogId = (Integer) map.get(PanoServer.PARAM_READING_LOG_ID);
if (map.containsKey(ABookKeys.PARAM_READING_LOG_ID)) {
readingLogId = (Integer) map.get(ABookKeys.PARAM_READING_LOG_ID);
}
return null;
}
});
if (ret != null && !ret.startsWith(PanoServer.OK)) {
if (ret != null && !ret.startsWith(ABookKeys.OK)) {
showSimpleAlertDialog(getRString(R.string.error), ret);
}
} else if (url.startsWith(PanoServer.GETITS_OPEN_SHARE)) {
} else if (url.startsWith(ABookKeys.GETITS_OPEN_SHARE)) {
Map<String, String> param = new HashMap<String, String>();
for (String key : uri.getQueryParameterNames()) {
param.put(key, uri.getQueryParameter(key));
}
String shareUrl = null;
if (param.containsKey(PanoServer.PARAM_SHARE_URL)) {
shareUrl = (String) param.get(PanoServer.PARAM_SHARE_URL);
if (param.containsKey(ABookKeys.PARAM_SHARE_URL)) {
shareUrl = (String) param.get(ABookKeys.PARAM_SHARE_URL);
}
if (beaconDialog != null && beaconDialog.isShowing()) {
Logger.d(TAG, "Already Open Beacon Dialog");
......@@ -449,7 +439,7 @@ public class HTMLXWalkWebViewActivity extends ParentWebViewActivity {
beaconDialog.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int whichButton) {
javaScriptSignageCmd(PanoServer.CMD_GETITS_CLOSE_SHARE, webView);
javaScriptSignageCmd(ABookKeys.CMD_GETITS_CLOSE_SHARE, webView);
dialog.dismiss();
}
});
......@@ -459,7 +449,7 @@ public class HTMLXWalkWebViewActivity extends ParentWebViewActivity {
}
}
return true;
} else if (url.startsWith(PanoServer.PING)) {
} else if (url.startsWith(ABookKeys.PING)) {
return true;
}
return false;
......
package jp.agentec.abook.abv.ui.viewer.activity;
import android.content.ActivityNotFoundException;
import android.content.DialogInterface;
import android.os.Build;
import android.os.Bundle;
import android.widget.TextView;
import jp.agentec.abook.abv.bl.common.log.Logger;
import jp.agentec.abook.abv.launcher.android.R;
import jp.agentec.abook.abv.ui.common.dialog.ABookAlertDialog;
import jp.agentec.abook.abv.ui.common.util.AlertDialogUtil;
import jp.agentec.abook.abv.ui.home.helper.PanoViewHelper;
/**
* 360度コンテンツを表示する画面
* 内部サーバーを作成して起動させる
* Created by jang on 2016/03/07.
*/
public class PanoViewActivity extends NoPdfViewActivity {
private static final String TAG = PanoViewActivity.class.getSimpleName();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) {// 4.2.2
showAlertMessage(R.string.msg_require_ver422);
return;
}
try {
PanoViewHelper.getInstance().startPanoContent(this, path, contentId);
} catch (ActivityNotFoundException e) {
showAlertMessage(R.string.msg_require_chrome);
return;
}
Logger.d(TAG, "[startPanoContent]");
TextView message = (TextView) findViewById(R.id.txt_message);
message.setText(getString(R.string.msg_pano_viewer));
}
private void showAlertMessage(int messageId) {
final ABookAlertDialog alertDialog = AlertDialogUtil.createAlertDialog(this, getString(R.string.confirm), getString(messageId));
alertDialog.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
finishActivity();
}
});
showAlertDialog(alertDialog);
}
}
package jp.agentec.abook.abv.ui.viewer.activity;
import android.app.Activity;
import android.content.Context;
import android.content.DialogInterface;
import android.net.Uri;
import android.view.View;
......@@ -12,15 +14,22 @@ import android.widget.RelativeLayout;
import android.widget.TextView;
import java.io.File;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import jp.agentec.abook.abv.bl.acms.type.ProjectType;
import jp.agentec.abook.abv.bl.common.ABVEnvironment;
import jp.agentec.abook.abv.bl.common.Callback;
import jp.agentec.abook.abv.bl.common.CommonExecutor;
import jp.agentec.abook.abv.bl.common.Constant;
import jp.agentec.abook.abv.bl.common.constant.ABookKeys;
import jp.agentec.abook.abv.bl.common.log.Logger;
import jp.agentec.abook.abv.bl.logic.AbstractLogic;
import jp.agentec.abook.abv.bl.logic.ContentObjectLogLogic;
import jp.agentec.abook.abv.bl.logic.ProjectLogic;
import jp.agentec.abook.abv.cl.util.ContentLogUtil;
import jp.agentec.abook.abv.cl.util.PreferenceUtil;
import jp.agentec.abook.abv.launcher.android.R;
import jp.agentec.abook.abv.ui.common.activity.ABVContentViewActivity;
import jp.agentec.abook.abv.ui.common.activity.ABVUIActivity;
......@@ -30,6 +39,10 @@ import jp.agentec.abook.abv.ui.common.constant.ErrorMessage;
import jp.agentec.abook.abv.ui.common.dialog.ABookAlertDialog;
import jp.agentec.abook.abv.ui.common.util.AlertDialogUtil;
import jp.agentec.abook.abv.ui.home.helper.ABookCheckWebViewHelper;
import jp.agentec.abook.abv.ui.home.helper.ActivityHandlingHelper;
import jp.agentec.abook.abv.ui.home.helper.ContentViewHelper;
import jp.agentec.adf.util.DateTimeFormat;
import jp.agentec.adf.util.DateTimeUtil;
import jp.agentec.adf.util.FileUtil;
public class ParentWebViewActivity extends ABVContentViewActivity {
......@@ -330,4 +343,75 @@ public class ParentWebViewActivity extends ABVContentViewActivity {
return mProjectId;
}
public String doParameter(Map<String, String> param, Context context, long contentId, Integer lastPageNo, Integer readingLogId, Date lastPageStartDate, Callback callback) {
String uri = param.get(ABookKeys.URI);
Long dstContentId = Long.valueOf(param.get("contentId"));
Integer pageNo = param.containsKey("pageNo")? Integer.valueOf(param.get("pageNo")) - 1: 0; // 1スタートを0スタート
if (uri.startsWith(ABookKeys.CONTENTLINK)) {
int result = ContentViewHelper.getInstance().linkDownloadChecked(dstContentId, pageNo);
Logger.d(TAG, "linkDownloadChecked result=%s", result);
if (result == ContentViewHelper.LinkContentStatus.NoExist) {
return context.getString(R.string.msg_require_content_refresh);
}
ContentLogUtil.getInstance().endContentPageReadLog(contentId, lastPageNo, readingLogId);
if (result == ContentViewHelper.LinkContentStatus.DownloadedContent) {
Activity currentActivity = ActivityHandlingHelper.getInstance().getContentViewActivity();
// HTMLWebViewActivityの場合はfinish()を実施して、コンテンツを開く
if (currentActivity instanceof HTMLWebViewActivity) {
currentActivity.finish();
}
ActivityHandlingHelper.getInstance().startContentActivity(dstContentId, pageNo);
} else {
int prePage = 0;
ABVContentViewActivity viewActivity = ActivityHandlingHelper.getInstance().getContentViewActivity();
if (viewActivity != null) {
prePage = viewActivity.getCurrentPageNumber();
}
if (ActivityHandlingHelper.getInstance().checkContent(dstContentId, prePage)) {
ActivityHandlingHelper.getInstance().startContentActivity(dstContentId, prePage);
}
return null;
}
} else if (uri.startsWith(ABookKeys.DETAILLOG)) {
if (readingLogId == null) { // 開始
readingLogId = ContentLogUtil.getInstance().startContentReadLog(context, contentId, PreferenceUtil.getUserPref(context, AppDefType.DefPrefKey.PERMISSION_ACCESS_LOCATION, false));
}
AbstractLogic.getLogic(ContentObjectLogLogic.class).insertContentObjectLog(contentId, readingLogId, pageNo, param);
} else if (uri.startsWith(ABookKeys.MOVEPAGE)) { // ブラウザから送られてきた時間は使わず、アプリの時間を用いる
if (readingLogId == null) { // 開始ページ
readingLogId = ContentLogUtil.getInstance().startContentReadLog(context, contentId, PreferenceUtil.getUserPref(context, AppDefType.DefPrefKey.PERMISSION_ACCESS_LOCATION, false));
ContentLogUtil.getInstance().startContentPageReadLog(contentId, readingLogId, pageNo);
} else {
if (param.get("readingEndDate") != null) { // 終了時刻有
ContentLogUtil.getInstance().endContentPageReadLog(contentId, pageNo, readingLogId);
} else {
ContentLogUtil.getInstance().endContentPageReadLog(contentId, lastPageNo, readingLogId); // crosswalkのときもjsではローカルサーバに送るが受取がないためここで終了を呼び出す。ただしローカルサーバを使う場合2重に処理が走る。
ContentLogUtil.getInstance().startContentPageReadLog(contentId, readingLogId, pageNo);
}
}
String[] readingStartDate = param.get("readingStartDate").split(",");
Date pageStartDate = DateTimeUtil.toDate(readingStartDate[0], readingStartDate[1], DateTimeFormat.yyyyMMddHHmmss_hyphen);
if (lastPageStartDate == null || lastPageStartDate.before(pageStartDate)) {
lastPageStartDate = pageStartDate;
lastPageNo = pageNo;
}
}
Map<String, Object> ret = new HashMap<String, Object>();
if (lastPageStartDate != null) {
ret.put("lastPageStartDate", lastPageStartDate);
}
if (lastPageNo != null) {
ret.put("lastPageNo", lastPageNo);
}
if (readingLogId != null) {
ret.put("readingLogId", readingLogId);
}
callback.callback(ret);
return null;
}
}
......@@ -23,7 +23,7 @@ import android.widget.TextView;
public class PDFIndexDialog {
// private static final String TAG = "PDFIndexDialog";
private static final String TAG = "PDFIndexDialog";
private final int ROOT_ID = 0; // ルートID
private List<ContentPDFIndexDto> dataList; // データリスト
private ContentViewActivity activity; // 呼出元アクティビティ
......
......@@ -84,8 +84,6 @@ follow_service_option=true
is_check_invalid_passward_limit=true
#リピート再生のデフォルト値
repeat_default=true
#360°コンテンツ用のWebサーバポート
pano_web_server_port=28080
#Setting Info(設定画面のABookについての設定情報)
version_name=1.9.401
......@@ -105,9 +103,6 @@ app_log_retention=7
hprof=false
repository_fqdn=s.abook.bz
default_content_key=0000
Beacon_UUID=
beacon_scan_interval=3000
# abvFunctionOptions.xml
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment