package net.momodalo.app.vimtouch;
import android.app.Activity;
import android.app.DownloadManager;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.widget.ProgressBar;
import android.widget.TextView;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.InputStream;
import java.lang.ref.WeakReference;
import java.math.BigInteger;
import java.security.DigestInputStream;
import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
import net.momodalo.app.vimtouch.R;
import net.momodalo.app.vimtouch.addons.PluginAddOn;
import net.momodalo.app.vimtouch.addons.PluginFactory;
import net.momodalo.app.vimtouch.addons.RuntimeAddOn;
import net.momodalo.app.vimtouch.addons.RuntimeFactory;
public class InstallProgress extends Activity {
public static final String LOG_TAG = "VIM Installation";
private Uri mUri;
private ProgressBar mProgressBar;
private TextView mProgressText;
private void installDefaultRuntime() {
ArrayList<RuntimeAddOn> runtimes = RuntimeFactory.getAllRuntimes(getApplicationContext());
try{
if(runtimes.size() == 0) {
MessageDigest md = MessageDigest.getInstance("MD5");
InputStream is = new DigestInputStream(getResources().openRawResource(R.raw.vim),md);
installZip(is, null, "Default Runtime");
// write md5 bytes
File md5 = new File(getMD5Filename(this));
FileWriter fout = new FileWriter(md5);
BigInteger bi = new BigInteger(1, md.digest());
String result = bi.toString(16);
if (result.length() % 2 != 0)
result = "0"+result;
Log.e(LOG_TAG, "compute md5 "+result);
fout.write(result);
fout.close();
}else{
Context context = getApplicationContext();
for (RuntimeAddOn rt: runtimes){
if(!rt.isInstalled(context)){
InputStream input = rt.getPackageContext().getAssets().openFd(rt.getAssetName()).createInputStream();
rt.initTypeDir(context);
FileWriter fw = new FileWriter(rt.getFileListName(context));
installZip(input,fw, rt.getDescription());
fw.close();
rt.setInstalled(context,true);
}
}
}
installZip(getResources().openRawResource(R.raw.terminfo),null, "Terminfo");
File folder = new File(this.getApplicationContext().getFilesDir()+"/vim");
if (!folder.exists()) { // Make folder
try {
folder.mkdirs();
} catch (Exception e) {
Log.e("folder", "Failed to create folder", e);
}
}
installSysVimrc(this);
} catch(Exception e) {
Log.e(LOG_TAG, "install vim runtime or compute md5 error", e);
}
}
private static String getVimrc(Activity activity) {
return activity.getApplicationContext().getFilesDir()+"/vim/vimrc";
}
private static String getMD5Filename( Activity activity) {
return activity.getApplicationContext().getFilesDir()+"/vim.md5";
}
private static boolean checkMD5(Activity activity){
ArrayList<RuntimeAddOn> runtimes = RuntimeFactory.getAllRuntimes(activity.getApplicationContext());
if(runtimes.size() > 0) return true;
File md5 = new File(getMD5Filename(activity));
if(!md5.exists()){
Log.w(LOG_TAG, "No MD5 file");
return false;
}
// read md5
try{
BufferedReader reader = new BufferedReader(new FileReader(md5));
String saved = reader.readLine();
Log.w(LOG_TAG, "Compare "+activity.getResources().getString(R.string.vim_md5)+" and "+saved);
if(saved.equals(activity.getResources().getString(R.string.vim_md5))) return true;
}catch(Exception e){
Log.e(LOG_TAG, "MD5 file error", e);
}
return false;
}
public static boolean isInstalled(Activity activity){
// check runtimes which not installed yet first
ArrayList<RuntimeAddOn> runtimes = RuntimeFactory.getAllRuntimes(activity.getApplicationContext());
for (RuntimeAddOn rt: runtimes){
if(!rt.isInstalled(activity.getApplicationContext())){
Log.w(LOG_TAG, "Not installed runtime: "+rt);
return false;
}
}
File vimrc = new File(getVimrc(activity));
if(vimrc.exists()){
// Compare size to make sure the sys vimrc doesn't change
try{
if(fileSize(vimrc) != activity.getResources().openRawResource(R.raw.vimrc).available()){
installSysVimrc(activity);
}
}catch(Exception e){
installSysVimrc(activity);
}
Log.w(LOG_TAG, "MD5 error: "+vimrc.getAbsolutePath()+" - "+checkMD5(activity));
return checkMD5(activity);
}
Log.w(LOG_TAG, "No vimrc: "+vimrc.getAbsolutePath());
return false;
}
private static long fileSize(File file) {
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) { // 2.3+
return file.getTotalSpace();
}
return file.length();
}
public static void installSysVimrc(Activity activity) {
File vimrc = new File(activity.getApplicationContext().getFilesDir()+"/vim/vimrc");
try{
BufferedInputStream is = new BufferedInputStream(activity.getResources().openRawResource(R.raw.vimrc));
FileWriter fout = new FileWriter(vimrc);
while(is.available() > 0){
fout.write(is.read());
}
fout.close();
} catch(Exception e) {
Log.e(LOG_TAG, "install vimrc", e);
}
File tmp = new File(activity.getApplicationContext().getFilesDir()+"/tmp");
tmp.mkdir();
}
private void installLocalFile() {
try {
File file = new File(mUri.getPath());
if(file.exists()){
installZip(new FileInputStream(file), null, mUri.getPath());
}
}catch (Exception e){
Log.e(LOG_TAG, "install " + mUri + " error " + e);
}
}
static final int MSG_SET_TEXT = 1;
static class ProgressHandler extends Handler {
private final WeakReference<InstallProgress> mActivity;
ProgressHandler(InstallProgress activity) {
mActivity = new WeakReference<InstallProgress>(activity);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_SET_TEXT:
String res = (String)msg.obj;
InstallProgress activity = mActivity.get();
if (activity != null)
activity.mProgressText.setText(res);
activity.setTitle(res);
break;
}
}
}
private ProgressHandler mHandler = new ProgressHandler(this);
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
try {
mUri = getIntent().getData();
}catch (Exception e){
mUri = null;
}
setContentView(R.layout.installprogress);
mProgressBar = (ProgressBar) findViewById(R.id.progress_bar);
mProgressText = (TextView) findViewById(R.id.progress_text);
// Start lengthy operation in a background thread
new Thread(new Runnable() {
public void run() {
Log.e(LOG_TAG, "install " + mUri );
Context context = getApplicationContext();
if(mUri == null){
installDefaultRuntime();
/*
}else if (mUri.getScheme().equals("http") ||
mUri.getScheme().equals("https") ||
mUri.getScheme().equals("ftp")) {
downloadRuntime(mUri);
*/
}else if (mUri.getScheme().equals("backup")){
File output = new File(mUri.getPath());
backupAll(output);
showNotification(R.string.backup_finish);
}else if (mUri.getScheme().equals("file")) {
installLocalFile();
showNotification(R.string.install_finish);
}else if (mUri.getScheme().equals("plugin")){
PluginAddOn plugin = PluginFactory.getPluginById( mUri.getAuthority(), context);
try{
InputStream input = plugin.getPackageContext().getAssets().openFd(plugin.getAssetName()).createInputStream();
plugin.initTypeDir(context);
FileWriter fw = new FileWriter(plugin.getFileListName(context));
installZip(input,fw, plugin.getDescription());
fw.close();
plugin.setInstalled(context,true);
showNotification(R.string.install_finish);
}catch(Exception e){
}
}else if (mUri.getScheme().equals("runtime")){
RuntimeAddOn runtime = RuntimeFactory.getRuntimeById( mUri.getAuthority(), context);
try{
InputStream input = runtime.getPackageContext().getAssets().openFd(runtime.getAssetName()).createInputStream();
runtime.initTypeDir(context);
FileWriter fw = new FileWriter(runtime.getFileListName(context));
installZip(input,fw, runtime.getDescription());
fw.close();
runtime.setInstalled(context,true);
showNotification(R.string.install_finish);
}catch(Exception e){
}
}else if (mUri.getScheme().equals("content")){
try{
InputStream attachment = getContentResolver().openInputStream(mUri);
installZip(attachment, null, " from other application");
showNotification(R.string.install_finish);
}catch(Exception e){
}
}
// check plugins which not installed yet first
ArrayList<PluginAddOn> plugins = PluginFactory.getAllPlugins(getApplicationContext());
for (PluginAddOn plugin: plugins){
if(!plugin.isInstalled(getApplicationContext())){
try{
InputStream input = plugin.getPackageContext().getAssets().openFd(plugin.getAssetName()).createInputStream();
plugin.initTypeDir(context);
FileWriter fw = new FileWriter(plugin.getFileListName(context));
installZip(input,fw, plugin.getDescription());
fw.close();
plugin.setInstalled(context,true);
showNotification(R.string.install_finish);
}catch(Exception e){
}
}
}
finish();
}
}).start();
}
void showNotification(int desc) {
String svc = NOTIFICATION_SERVICE;
NotificationManager nm = (NotificationManager)getSystemService(svc);
CharSequence from = "VimTouch";
CharSequence message = getString(desc);
Notification notif = new Notification(R.drawable.ic_vim_notification,
message, System.currentTimeMillis());
// The PendingIntent to launch our activity if the user selects this
// notification
Intent intent = new Intent(this, VimTouch.class);
PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
intent, 0);
notif.setLatestEventInfo(this, from, message, contentIntent);
notif.defaults = Notification.DEFAULT_SOUND
| Notification.DEFAULT_LIGHTS;
notif.flags |= Notification.FLAG_AUTO_CANCEL;
nm.notify(0, notif);
}
private void installZip(InputStream is, FileWriter fw, String desc) {
String dirname = getApplicationContext().getFilesDir().getPath();
int progress = 0;
mProgressBar.setProgress(0);
String msgText = getString(R.string.installing);
mHandler.sendMessage(mHandler.obtainMessage(MSG_SET_TEXT, msgText+" "+desc));
ZipInputStream zin = new ZipInputStream(new BufferedInputStream(is));
ZipEntry ze = null;
int size;
byte[] buffer = new byte[8192];
try {
int total = is.available();
mProgressBar.setMax(total);
while ((ze = zin.getNextEntry()) != null) {
Log.i(LOG_TAG, "Unzipping " + ze.getName());
if(ze.isDirectory()) {
File file = new File(dirname+"/"+ze.getName());
if(!file.isDirectory())
file.mkdirs();
if(ze.getName().startsWith("bin/")) {
setReadableExecutable(file);
}
} else {
File file = new File(dirname+"/"+ze.getName());
FileOutputStream fout = new FileOutputStream(file);
BufferedOutputStream bufferOut = new BufferedOutputStream(fout, buffer.length);
while((size = zin.read(buffer, 0, buffer.length)) != -1) {
bufferOut.write(buffer, 0, size);
}
bufferOut.flush();
bufferOut.close();
if(ze.getName().startsWith("bin/")) {
setReadableExecutable(file);
}
if(fw != null) fw.write(ze.getName()+"\n");
}
mProgressBar.setProgress(total-is.available());
}
byte[] buf = new byte[2048];
while(is.available() > 0){
is.read(buf);
mProgressBar.setProgress(total-is.available());
}
buf = null;
zin.close();
} catch(Exception e) {
Log.e(LOG_TAG, "unzip", e);
}
}
private static void setReadableExecutable(File file) {
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) { // 2.3+
file.setExecutable(true, false);
file.setReadable(true, false);
return;
}
}
private void backupAll(File dest){
String src = getApplicationContext().getFilesDir().getPath()+"/vim";
mProgressBar.setProgress(0);
String msgText = getString(R.string.backup);
mHandler.sendMessage(mHandler.obtainMessage(MSG_SET_TEXT, msgText));
try {
ZipOutputStream zip = null;
FileOutputStream fileWriter = null;
fileWriter = new FileOutputStream(dest);
zip = new ZipOutputStream(fileWriter);
mProgressBar.setProgress(0);
addFolderToZip("", src, zip);
zip.flush();
zip.close();
}catch(Exception e){
}
}
private void addFileToZip(String path, String srcFile, ZipOutputStream zip) throws Exception {
File folder = new File(srcFile);
if (folder.isDirectory()) {
addFolderToZip(path, srcFile, zip);
} else {
byte[] buf = new byte[1024];
int len;
FileInputStream in = new FileInputStream(srcFile);
zip.putNextEntry(new ZipEntry(path + "/" + folder.getName()));
while ((len = in.read(buf)) > 0) {
zip.write(buf, 0, len);
}
}
}
private void addFolderToZip(String path, String srcFolder, ZipOutputStream zip) throws Exception {
File folder = new File(srcFolder);
int total = folder.list().length;
int done = 0;
if (path.equals("")) {
mProgressBar.setMax(total);
}
for (String fileName : folder.list()) {
if (path.equals("")) {
addFileToZip(folder.getName(), srcFolder + "/" + fileName, zip);
mProgressBar.setProgress(++done);
} else {
addFileToZip(path + "/" + folder.getName(), srcFolder + "/" + fileName, zip);
}
}
}
private DownloadManager mDM;
private long mEnqueue = -1;
private BroadcastReceiver mReceiver = null;
public void onDestroy() {
if(mReceiver != null)
unregisterReceiver(mReceiver);
super.onDestroy();
}
}