/*
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 java.util.concurrent.TimeUnit;
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.text.util.Linkify;
import android.provider.Settings;
import android.app.AlertDialog;
import android.widget.ScrollView;
import android.content.DialogInterface;
import android.app.ProgressDialog;
import android.widget.Toast;
import android.net.Uri;
import java.util.*;
public class MainActivity extends Activity
{
private ScrollView scroll = null;
private LinearLayout layout = null;
private TextView text = null;
private Button cancelJob = null;
private Button reenablePrinters = null;
private Button openSettings = null;
private Button addPrinter = null;
private Button deletePrinter = null;
private Button sharePrinter = null;
private Button viewNetwork = null;
private String[] networkTree = null;
private Button advancedInterface = null;
private Button stopService = null;
private boolean destroyed = false;
private boolean jobsThreadStarted = false;
private boolean jobsThreadPaused = false;
private String[] printJobs = new String[0];
private Semaphore printJobsUpdate = new Semaphore(0);
private ProgressDialog progressCircle = null;
@Override protected void onCreate(Bundle savedInstanceState)
{
destroyed = false;
jobsThreadStarted = false;
super.onCreate(savedInstanceState);
Log.d(TAG, "onCreate");
reinitUI();
Installer.p = this;
Installer.text = text;
Installer.unpackData();
}
@Override synchronized protected void onDestroy()
{
Log.d(TAG, "onDestroy");
destroyed = true;
jobsThreadStarted = false;
super.onDestroy();
}
@Override protected void onStart()
{
super.onStart();
if (!Installer.unpacking)
enableSettingsButton();
jobsThreadPaused = false;
}
@Override protected void onStop()
{
super.onStop();
jobsThreadPaused = true;
}
void reinitUI()
{
layout = new LinearLayout(this);
layout.setOrientation(LinearLayout.VERTICAL);
layout.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT));
layout.setPadding(10, 10, 10, 10);
text = new TextView(this);
text.setText(R.string.init);
text.setText("Initializing");
text.setTextSize(20);
text.setPadding(20, 20, 20, 50);
layout.addView(text);
scroll = new ScrollView(this);
scroll.addView(layout);
progressCircle = new ProgressDialog(this);
progressCircle.setMessage(getResources().getString(R.string.please_wait));
setContentView(scroll);
}
void enableSettingsButton()
{
runOnUiThread(new Runnable()
{
public void run()
{
reinitUI();
enableSettingsButtonPriv();
if (!jobsThreadStarted)
{
updateActiveJobs();
printJobsUpdate.release();
}
jobsThreadStarted = true;
}
});
}
private void enableSettingsButtonPriv()
{
text.setText(CupsPrintService.pluginEnabled ? R.string.service_running : R.string.enable_plugin);
cancelJob = new Button(this);
cancelJob.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
cancelJob.setText(R.string.cancel_job);
cancelJob.setOnClickListener(new View.OnClickListener()
{
public void onClick(View v)
{
if (printJobs == null)
return;
AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
builder.setTitle(R.string.cancel_job);
builder.setItems(printJobs, new DialogInterface.OnClickListener()
{
public void onClick(DialogInterface dialog, final int which)
{
dialog.dismiss();
if (printJobs.length > which) // Just in case jobs list changes while user is waiting
Cups.cancelPrintJob(MainActivity.this, printJobs[which]);
cancelJob.setEnabled(false);
printJobsUpdate.release();
}
});
builder.setPositiveButton(R.string.close, new DialogInterface.OnClickListener()
{
public void onClick(DialogInterface d, int s)
{
d.dismiss();
}
});
AlertDialog alert = builder.create();
alert.setOwnerActivity(MainActivity.this);
alert.show();
}
});
cancelJob.setEnabled(false);
layout.addView(cancelJob);
reenablePrinters = new Button(this);
reenablePrinters.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
reenablePrinters.setText(R.string.restart_printing);
reenablePrinters.setOnClickListener(new View.OnClickListener()
{
public void onClick(View v)
{
reenablePrinters.setVisibility(View.GONE);
new Thread(new Runnable()
{
public void run()
{
String[] printers = Cups.getPrinters(MainActivity.this);
for (String p: printers)
{
Cups.enablePrinter(MainActivity.this, p);
}
}
}).start();
}
});
reenablePrinters.setVisibility(View.GONE);
layout.addView(reenablePrinters);
openSettings = new Button(this);
openSettings.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
openSettings.setText(getResources().getString(CupsPrintService.pluginEnabled ? R.string.settings_button : R.string.enable_plugin_button));
openSettings.setOnClickListener(new View.OnClickListener()
{
public void onClick(View v)
{
MainActivity.this.startActivity(new Intent(Settings.ACTION_PRINT_SETTINGS));
}
});
layout.addView(openSettings);
addPrinter = new Button(this);
addPrinter.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
addPrinter.setText(getResources().getString(R.string.add_printer_button));
addPrinter.setOnClickListener(new View.OnClickListener()
{
public void onClick(View v)
{
MainActivity.this.startActivity(new Intent(MainActivity.this, AddPrinterActivity.class));
}
});
layout.addView(addPrinter);
deletePrinter = new Button(this);
deletePrinter.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
deletePrinter.setText(getResources().getString(R.string.delete_printer_button));
deletePrinter.setOnClickListener(new View.OnClickListener()
{
public void onClick(View v)
{
final String[] printers = Cups.getPrinters(MainActivity.this);
AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
builder.setTitle(R.string.delete_printer_button);
builder.setItems(printers, new DialogInterface.OnClickListener()
{
public void onClick(DialogInterface dialog, final int which)
{
dialog.dismiss();
AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
builder.setTitle(getResources().getString(R.string.delete_printer_button) + " " + printers[which]);
builder.setPositiveButton(R.string.delete_printer_button, new DialogInterface.OnClickListener()
{
public void onClick(DialogInterface d, int s)
{
progressCircle.show();
Cups.deletePrinter(MainActivity.this, printers[which]);
Cups.updatePrintersInfo(MainActivity.this);
progressCircle.dismiss();
d.dismiss();
}
});
builder.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener()
{
public void onClick(DialogInterface d, int s)
{
d.dismiss();
}
});
AlertDialog alert = builder.create();
alert.setOwnerActivity(MainActivity.this);
alert.show();
}
});
builder.setPositiveButton(R.string.cancel, new DialogInterface.OnClickListener()
{
public void onClick(DialogInterface d, int s)
{
d.dismiss();
}
});
AlertDialog alert = builder.create();
alert.setOwnerActivity(MainActivity.this);
alert.show();
}
});
layout.addView(deletePrinter);
sharePrinter = new Button(this);
sharePrinter.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
sharePrinter.setText(getResources().getString(R.string.share_printer_address));
sharePrinter.setOnClickListener(new View.OnClickListener()
{
public void onClick(View v)
{
final String[] printers = Cups.getPrinters(MainActivity.this);
AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
builder.setTitle(R.string.share_printer_address);
builder.setItems(printers, new DialogInterface.OnClickListener()
{
public void onClick(DialogInterface dialog, final int which)
{
dialog.dismiss();
Intent intent = new Intent();
intent.setClass(MainActivity.this, SharePrinterActivity.class);
intent.putExtra("n", printers[which]);
startActivity(intent);
}
});
builder.setPositiveButton(R.string.close, new DialogInterface.OnClickListener()
{
public void onClick(DialogInterface d, int s)
{
d.dismiss();
}
});
AlertDialog alert = builder.create();
alert.setOwnerActivity(MainActivity.this);
alert.show();
}
});
layout.addView(sharePrinter);
viewNetwork = new Button(this);
viewNetwork.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
viewNetwork.setText(getResources().getString(R.string.view_network_button));
viewNetwork.setOnClickListener(new View.OnClickListener()
{
public void onClick(View v)
{
String[] networkTreeCopy = networkTree;
TextView text = new TextView(MainActivity.this);
String s = "No network shares found";
if (networkTreeCopy != null && networkTreeCopy.length > 0)
{
s = "";
for (String ss: networkTreeCopy)
s = s + ss + "\n";
}
text.setText(s);
text.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
text.setPadding(0, 5, 0, 5);
text.setTextSize(20.0f);
text.setGravity(Gravity.LEFT);
AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
ScrollView scroll = new ScrollView(MainActivity.this);
scroll.addView(text, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
Button ok = new Button(MainActivity.this);
final AlertDialog alertDismiss[] = new AlertDialog[1];
ok.setOnClickListener(new View.OnClickListener()
{
public void onClick(View v)
{
alertDismiss[0].dismiss();
}
});
ok.setText(R.string.ok);
Button scanAgain = new Button(MainActivity.this);
scanAgain.setOnClickListener(new View.OnClickListener()
{
public void onClick(View v)
{
alertDismiss[0].cancel();
updateNetworkTree();
}
});
scanAgain.setText(R.string.view_network_button_scan_again);
LinearLayout layout = new LinearLayout(MainActivity.this);
layout.setOrientation(LinearLayout.VERTICAL);
layout.addView(scroll);
layout.addView(ok);
layout.addView(scanAgain);
builder.setView(layout);
AlertDialog alert = builder.create();
alertDismiss[0] = alert;
alert.setOwnerActivity(MainActivity.this);
alert.show();
}
});
layout.addView(viewNetwork);
updateNetworkTree();
advancedInterface = new Button(this);
advancedInterface.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
advancedInterface.setText(getResources().getString(R.string.advanced_interface_button));
advancedInterface.setOnClickListener(new View.OnClickListener()
{
public void onClick(View v)
{
Toast.makeText(MainActivity.this, R.string.stop_service_after_changes, Toast.LENGTH_LONG).show();
startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("http://127.0.0.1:6631/")));
}
});
layout.addView(advancedInterface);
stopService = new Button(this);
stopService.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
stopService.setText(getResources().getString(R.string.stop_print_service));
stopService.setOnClickListener(new View.OnClickListener()
{
public void onClick(View v)
{
Toast.makeText(MainActivity.this, R.string.service_restarted_automatically, Toast.LENGTH_LONG).show();
finish();
new Thread(new Runnable()
{
public void run()
{
try
{
Thread.sleep(4000);
}
catch (Exception ee)
{
}
String busybox = new File(getFilesDir(), "busybox").getAbsolutePath();
new Proc(new String[] {busybox, "nohup", "setsid", "sh", "-c", "cd /proc/ ; for f in * ; do [ $f = $$ ] || kill -9 $f ; done"}, getFilesDir());
Cups.stopCupsDaemon(MainActivity.this);
System.exit(0);
}
}).start();
}
});
layout.addView(stopService);
}
private void updateNetworkTree()
{
viewNetwork.setEnabled(false);
viewNetwork.setText(getResources().getString(R.string.view_network_button_scanning));
new Thread(new Runnable()
{
public void run()
{
networkTree = Cups.getNetworkTree(MainActivity.this, "", "", "");
synchronized(MainActivity.this)
{
if (destroyed)
return;
runOnUiThread(new Runnable()
{
public void run()
{
if (viewNetwork == null || destroyed)
return;
viewNetwork.setEnabled(true);
viewNetwork.setText(getResources().getString(R.string.view_network_button));
}
});
};
}
}).start();
}
private void updateActiveJobs()
{
new Thread(new Runnable()
{
public void run()
{
try
{
printJobsUpdate.tryAcquire(4, TimeUnit.SECONDS);
}
catch (Exception e)
{
}
while (jobsThreadPaused)
{
if (destroyed)
return;
try
{
printJobsUpdate.tryAcquire(4, TimeUnit.SECONDS);
}
catch (Exception e)
{
}
}
final StringBuffer str = new StringBuffer();
String[] printers = Cups.getPrinters(MainActivity.this);
final ArrayList<String> jobList = new ArrayList<String>();
for (String p: printers)
{
Map<String, String[]> jobs = Cups.getPrintJobs(MainActivity.this, p, false);
for (String j: jobs.keySet())
{
str.append(j + ":\n");
jobList.add(j);
for (String stat: jobs.get(j))
str.append(stat + "\n");
}
}
Log.d(TAG, "updateActiveJobs: updating UI");
synchronized(MainActivity.this)
{
if (destroyed)
return;
runOnUiThread(new Runnable()
{
public void run()
{
if (destroyed)
return;
printJobs = jobList.toArray(new String[0]);
if (str.toString().length() == 0)
{
cancelJob.setEnabled(false);
text.setText(R.string.no_active_jobs);
reenablePrinters.setVisibility(View.GONE);
}
else
{
text.setText(str.toString());
cancelJob.setEnabled(true);
if (str.toString().contains("Alerts: printer-stopped"))
reenablePrinters.setVisibility(View.VISIBLE);
else
reenablePrinters.setVisibility(View.GONE);
}
updateActiveJobs();
}
});
}
}
}).start();
}
static public final String TAG = "MainActivity";
}