/*
Copyright (C) 2014 Sergii Pylypenko.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.cups.android;
import android.app.Activity;
import android.app.Service;
import android.content.Context;
import android.os.Bundle;
import android.os.IBinder;
import android.view.MotionEvent;
import android.view.KeyEvent;
import android.view.Window;
import android.view.WindowManager;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import android.widget.EditText;
import android.text.Editable;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.FrameLayout;
import android.graphics.drawable.Drawable;
import android.graphics.Color;
import android.content.res.Configuration;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Intent;
import android.view.View.OnKeyListener;
import android.view.MenuItem;
import android.view.Menu;
import android.view.Gravity;
import android.text.method.TextKeyListener;
import java.util.LinkedList;
import java.io.SequenceInputStream;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.FileOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.util.Set;
import android.text.SpannedString;
import java.io.BufferedReader;
import java.io.BufferedInputStream;
import java.io.InputStreamReader;
import android.view.inputmethod.InputMethodManager;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
import java.util.concurrent.Semaphore;
import android.content.pm.ActivityInfo;
import android.view.Display;
import android.text.InputType;
import android.util.Log;
import android.view.Surface;
import android.app.ProgressDialog;
import android.printservice.*;
import android.print.*;
import java.util.*;
import java.io.*;
import android.os.Environment;
import android.os.StatFs;
import java.net.URL;
import java.net.URLConnection;
public class Installer
{
static boolean unpacking = false;
public static MainActivity p = null;
public static TextView text = null;
synchronized static boolean isInstalled(Context p)
{
return new File(p.getFilesDir().getAbsolutePath() + Cups.IMG + Cups.CUPSD).exists();
}
static void unpackData()
{
if (unpacking)
return;
unpacking = true;
new Thread(new Runnable()
{
public void run()
{
unpackDataThread();
}
}).start();
}
synchronized static void unpackDataThread()
{
if (isInstalled(p))
{
unpacking = false;
Cups.startCupsDaemon(p);
p.enableSettingsButton();
return;
}
Log.i(TAG, "Extracting CUPS data");
setText(p.getResources().getString(R.string.please_wait_unpack));
StatFs storage = new StatFs(p.getFilesDir().getPath());
long avail = (long)storage.getAvailableBlocks() * storage.getBlockSize() / 1024 / 1024;
long needed = 600;
Log.i(TAG, "Available free space: " + avail + " Mb required: " + needed + " Mb");
if (avail < needed)
{
setText(p.getResources().getString(R.string.not_enough_space, needed, avail));
return;
}
try
{
String busybox = new File(p.getFilesDir(), "busybox").getAbsolutePath();
InputStream stream;
OutputStream out;
p.getFilesDir().mkdirs();
String ARCH = android.os.Build.CPU_ABI;
try
{
stream = p.getAssets().open("busybox-" + ARCH);
out = new FileOutputStream(busybox);
Cups.copyStream(stream, out);
}
catch (Exception eeeee)
{
try
{
ARCH = android.os.Build.CPU_ABI2;
stream = p.getAssets().open("busybox-" + ARCH);
out = new FileOutputStream(busybox);
Cups.copyStream(stream, out);
}
catch (Exception eeeeee)
{
throw new Exception("Unsupported architecture: " + android.os.Build.CPU_ABI);
}
}
new Proc(new String[] {"/system/bin/chmod", "0755", busybox}, p.getFilesDir());
try
{
InputStream archiveAssets = p.getAssets().open("dist-cups-jessie.tar.xz");
Process proc = Runtime.getRuntime().exec(new String[] {busybox, "tar", "xJ"}, null, p.getFilesDir());
copyStreamWithProgress(130000000, archiveAssets, proc.getOutputStream());
int status = proc.waitFor();
Log.i(TAG, "Unpacking data from assets: status: " + status);
}
catch (Exception e)
{
Log.i(TAG, "Error unpacking data from assets: " + e.toString());
Log.i(TAG, "No data archive in assets, trying OBB data");
try
{
File obbFile = new File(p.getExternalFilesDir(null).getParentFile().getParentFile().getParentFile(),
"obb/" + p.getPackageName() + "/main.100." + p.getPackageName() + ".obb");
Log.i(TAG, "OBB file path: " + obbFile.getAbsolutePath() + " exists " + obbFile.exists() + " length " + obbFile.length());
if (!obbFile.exists() || obbFile.length() < 256)
throw new IOException("Cannot find data file: " + obbFile.getAbsolutePath());
Process proc = Runtime.getRuntime().exec(new String[] {busybox, "tar", "xJ"}, null, p.getFilesDir());
copyStreamWithProgress(obbFile.length(), new FileInputStream(obbFile), proc.getOutputStream());
int status = proc.waitFor();
Log.i(TAG, "Extract data file: " + obbFile.getAbsolutePath() + " extract command status " + status);
// Clear the .obb file, we do not need it anymore
Proc pp = new Proc(new String[] {busybox, "sh", "-c", "echo Unpacked_and_truncated > " + obbFile.getAbsolutePath()}, p.getFilesDir());
Log.i(TAG, "Truncate data file: " + obbFile.getAbsolutePath() + " status " + pp.status + " " + Arrays.toString(pp.out));
}
catch (Exception ee)
{
final String ARCHIVE_URL = "http://sourceforge.net/projects/libsdl-android/files/ubuntu/CUPS/dist-cups-jessie.tar.xz/download";
Log.i(TAG, "Error unpacking data from OBB: " + ee.toString());
Log.i(TAG, "No data archive in OBB, downloading from web: " + ARCHIVE_URL);
setText(p.getResources().getString(R.string.downloading_web));
URL link = new URL(ARCHIVE_URL);
URLConnection connection = link.openConnection();
InputStream download = new BufferedInputStream(connection.getInputStream());
Process proc = Runtime.getRuntime().exec(new String[] {busybox, "tar", "xJ"}, null, p.getFilesDir());
copyStreamWithProgress(connection.getContentLength(), download, proc.getOutputStream());
int status = proc.waitFor();
Log.i(TAG, "Downloading from web: status: " + status);
}
}
new Thread(new Runnable()
{
public void run()
{
String dots = "";
while (unpacking)
{
setText(p.getResources().getString(R.string.please_wait_unpack) + dots);
dots += ".";
try
{
Thread.sleep(1000);
}
catch (InterruptedException e)
{
}
}
}
}).start();
new Proc(new String[] {busybox, "cp", "-af", "img-" + ARCH + "/.", "img/"}, p.getFilesDir());
new Proc(new String[] {busybox, "rm", "-rf", "img-armeabi-v7a", "img-x86"}, p.getFilesDir());
stream = p.getAssets().open("cupsd.conf");
out = new FileOutputStream(new File(Cups.chrootPath(p), "etc/cups/cupsd.conf"));
Cups.copyStream(stream, out);
new Proc(new String[] {busybox, "chmod", "-R", "go+rX", "usr/share/cups"}, Cups.chrootPath(p));
Log.i(TAG, "Extracting data finished");
}
catch(Exception e)
{
Log.i(TAG, "Error extracting data: " + e.toString());
unpacking = false;
setText(p.getResources().getString(R.string.error_extracting) + " " + e.toString());
return;
}
Cups.getPrinterModels(p);
unpacking = false;
p.enableSettingsButton();
Cups.startCupsDaemon(p);
}
static void setText(final String str)
{
p.runOnUiThread(new Runnable()
{
public void run()
{
text.setText(str);
}
});
}
static void copyStreamWithProgress(long size, InputStream stream, OutputStream out) throws java.io.IOException
{
byte[] buf = new byte[131072];
if (size <= 0)
size = 129332656;
setText(p.getResources().getString(R.string.please_wait_unpack_progress, 0));
int len = stream.read(buf);
long totalLen = 0;
while (len >= 0)
{
if(len > 0)
out.write(buf, 0, len);
totalLen += len;
setText(p.getResources().getString(R.string.please_wait_unpack_progress, totalLen * 100 / size));
len = stream.read(buf);
}
stream.close();
out.close();
setText(p.getResources().getString(R.string.please_wait_unpack_progress, 100));
}
static final String TAG = "CupsInstaller";
}