Menad�er zada� (Android Studio)

* Uwaga od Android 5.0 Lollipop mo�liwo�� pobierania uruchomionych proces�w zosta�a ograniczona tylko do bie��cej aplikacji
  Nale�y utworzy� wirtualne urz�dzenie z Android 4.0.3 IceCreamSandwich
  Aby mo�na je by�o po��czy�: Tools, Android, wy��czy� Enable ADB Integration (Android Debug Bridge)

[Na bazie projektu Paw�a Kruzy]

1. Tworzymy projekt typu Android Project:

Nazwa = MenedzerZadan
Package name = pl.umk.fizyka
Minimum SDK: API 15 (Android 4.0.3)
Activity: Empty Activity o nazwie "Menedzer"

Uwaga! Poza pobieraniem pami�ci u�ywanej przez procesy, wszystko jest zgodne z API 4 (Android 1.6) !!!

2. Interfejs b�dzie oparty o list� ListView

Przechodzimy do edycji pliku res\layout\activity_menedzer.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="pl.umk.fizyka.menedzerzadan.MenedzerActivity">

    <ListView
        android:id="@+id/listView1"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" />

</android.support.constraint.ConstraintLayout>


3. W pliku res/values/strings.xml zmieniamy app_name na "Dzia�aj�ce procesy:"
   (warto�� wykorzystywana w nag��wku)

<resources>
    <string name="app_name">Dzia�aj�ce procesy:</string>
</resources>

4. W pliku MenedzerActivity.java

a) tworzymy pole odpowiadaj�ce li�cie (kontrolce) i li�cie proces�w (pole b�dzie potrzebne w klasach wewn�trznych):

ListView lista;
List<ActivityManager.RunningAppProcessInfo> listaProcesow;

Konieczny jest import java.util.List, android.widget.ListView i android.app.ActivityManager.

b) W metodzie MenedzerZadanActivity.onCreate wi��emy je z list� w pliku XML

lista=(ListView)findViewById(R.id.listView1);

c) Tworzymy metod� pobieraj�c� list� proces�w i metody pomocnicze:

Importy:

import java.util.List;
import android.app.ActivityManager; //tu mozna zdobyc wiecej informacji np. wolna pamiec
import android.app.ActivityManager.RunningAppProcessInfo;
import android.widget.ArrayAdapter;

Metoda:

private List<RunningAppProcessInfo> pobierzListeProcesow() 
{
    ActivityManager am = (ActivityManager) this.getSystemService(ACTIVITY_SERVICE);
    return am.getRunningAppProcesses();
}

d) Metoda wy�wietlaj�ca list� proces�w w ListView:

private void wypelnijListeNazwamiProcesow(ListView lista)
{
    //wczesniej trzeba bedzie wywolac: listaProcesow=pobierzListeProcesow();
    if(listaProcesow==null || listaProcesow.size()==0) return;

    int liczbaProcesow=listaProcesow.size();
    String[] nazwyProcesow = new String[liczbaProcesow];
    for (int i = 0; i < liczbaProcesow; i++) 
    {
        RunningAppProcessInfo proces=listaProcesow.get(i);
        nazwyProcesow[i] = proces.processName+" ("+proces.pid+")";        	
    }
    lista.setAdapter(
        new ArrayAdapter<String>(
            this, 
            android.R.layout.simple_list_item_1, 
            nazwyProcesow));
}

e) Wywo�anie obu powy�szych metod dodajemy do onCreate:

listaProcesow = pobierzListeProcesow();
lista=(ListView)findViewById(R.id.listView1);        
wypelnijListeNazwamiProcesow(lista, listaProcesow);

** Uruchom i przetestuj
   W Android 5 zobaczymy tylko bie��cy proces, podczas gdy we wcze�niejszych - wi�cej proces�w

--> KONKURS: Przygotowa� metod� pobierzListeProcesow (+1 do oceny), StackOverflow

4. Klikni�cie elementu listy -> informacja o zajmowanej pami�ci:

Metoda pomocnicza:

    private int pamiecZajmowanaPrzezProces(int pid)
    {
        ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
        int tablica_numerow_pid[] = {pid};
        android.os.Debug.MemoryInfo[] memoryInfoArray = am.getProcessMemoryInfo(tablica_numerow_pid); //min API: 5
        return memoryInfoArray[0].getTotalSharedDirty(); //kB, min API: 5
    }

Zmiana w metodzie MenedzerActivity.onCreate:

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        lista=(ListView)findViewById(R.id.listView1);        
        wypelnijListeNazwamiProcesow(lista, pobierzListeProcesow());
        lista.setOnItemClickListener(new OnItemClickListener() 
        {
        	public void onItemClick(AdapterView<?> parent, View view, int position, long id) 
        	{
        		RunningAppProcessInfo proces=listaProcesow.get(position);
        		int pid = proces.pid;
        		String tekst = "Informacje o procesie\n";
        		tekst+="PID: " + pid + "\n";
        		tekst+="Ilo�� zajmowanej pami�ci: "+pamiecZajmowanaPrzezProces(pid)+" kb\n";
        		tekst+="Priorytet podstawowy: "+proces.importance;
                Toast toast = Toast.makeText(getApplicationContext(), tekst, Toast.LENGTH_LONG);
                toast.show();
            }
        });
    }

---------------------------------------------------------------------------------------------

Zamiast toast�w z opisem procesu, informacje o procesie do��czymy do listy (w�asne u�o�enie/layout listy)

1. W res/layout tworzymy plik wiersz.xml z w�asnym u�o�eniem wiersza listy:

Prawym klawiszem na pozycj� res\layout i z menu kontekstowego wybieramy New, XML, Layout XML File
W oknie New Android Component wpisujemy
Layout File Name: wiersz (bez .xml)
Root Tag: LinearLayout
Klikamy Finish
Kod pliku uzupe�niamy:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >
    
    <TextView
            android:id="@+id/nazwa"
            android:textSize="18sp"
            android:textColor="#000000"
            android:layout_width="fill_parent"
            android:layout_height="0dip"
            android:layout_weight="3" />
    <TextView
            android:id="@+id/opis"
            android:textSize="10sp"
            android:textColor="#000000"
            android:layout_width="fill_parent"
            android:layout_height="0dip"
            android:layout_weight="2"             
            android:ellipsize="marquee"
            android:maxLines="1" />    

</LinearLayout>

Zapisz do pliku!

2. W pliku MenedzerZadanActivity.java zast�pujemy metod� wypelnijListeNazwamiProcesow przez

        private class ProcesInfo
	{
		public String nazwa;
		public String opis;
	}
	
	private class ProcesyZOpisamiAdapter extends ArrayAdapter<ProcesInfo> 
	{
        private ArrayList<ProcesInfo> items;

        public ProcesyZOpisamiAdapter(Context context, int textViewResourceId, ArrayList<ProcesInfo> items) 
        {
        	super(context, textViewResourceId, items);
            this.items = items;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) 
        {
        	View v = convertView;
            if (v == null) 
            {
            	LayoutInflater vi = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                v = vi.inflate(R.layout.wiersz, null);
            }
            ProcesInfo o = items.get(position);
            if (o != null) 
            {
            	TextView nazwa = (TextView)v.findViewById(R.id.nazwa);
            	if (nazwa != null) nazwa.setText(o.nazwa);
            	
            	TextView opis = (TextView)v.findViewById(R.id.opis);                
                if(opis != null) opis.setText(o.opis);
            }
            return v;
        }
	} 
	
	private String opisPriorytetu(int priorytet)
	{
		switch(priorytet)
		{
		case RunningAppProcessInfo.IMPORTANCE_FOREGROUND: return "Pierwszy plan";
		case RunningAppProcessInfo.IMPORTANCE_VISIBLE: return "Widoczny";
		case RunningAppProcessInfo.IMPORTANCE_SERVICE: return "Us�uga";
		case RunningAppProcessInfo.IMPORTANCE_BACKGROUND: return "W tle";
		case RunningAppProcessInfo.IMPORTANCE_EMPTY: return "Pusty";
		default: return "";
		}
	}
	
	private void wypelnijListeNazwamiProcesowZOpisem(ListView lista)
	{
		int liczbaProcesow=listaProcesow.size();
		ArrayList<ProcesInfo> opisyProcesow=new ArrayList<ProcesInfo>(liczbaProcesow);
        //for (int i = 0; i < liczbaProcesow; i++) 
        //{
        //	RunningAppProcessInfo proces=listaProcesow.get(i);
        for(RunningAppProcessInfo proces : listaProcesow)
        {
        	ProcesInfo opisProcesu=new ProcesInfo();        	
        	opisProcesu.nazwa = proces.processName;
        	int pid = proces.pid;
    		String tekst = "";
    		tekst+="PID: " + pid + ", ";
    		tekst+="Pami��: "+pamiecZajmowanaPrzezProces(pid)+" kb, ";
    		tekst+="Priorytet: "+opisPriorytetu(proces.importance)+" ("+proces.importance+")";
    		opisProcesu.opis = tekst;
        	opisyProcesow.add(opisProcesu);
        }
        ProcesyZOpisamiAdapter a=new ProcesyZOpisamiAdapter(this,R.layout.wiersz,opisyProcesow); 
        lista.setAdapter(a);
	}

        private void odswiezListe()
        {            	
		listaProcesow=pobierzListeProcesow();
		lista.setAdapter(null);
		wypelnijListeNazwamiProcesowZOpisem(lista);
	}


3. Wy�wietl now� posta� listy i zwi�� z jej elementami z oknem dialogowym (AlertDialog) pytaj�cym 
o zabicie procesu. 

Uwaga! Zabijanie innego procesu nie jest czym�, co aplikacja w systemie Android powinna robi�.

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        listaProcesow=pobierzListeProcesow();
        lista=(ListView)findViewById(R.id.listView1);        

	//wypelnijListeNazwamiProcesow(lista);
        wypelnijListeNazwamiProcesowZOpisem(lista);
        lista.setOnItemClickListener(new AdapterView.OnItemClickListener() 
        {
        	public void onItemClick(AdapterView<?> parent, View view, int position, long id) 
        	{
        		RunningAppProcessInfo proces=listaProcesow.get(position);
        		final int pid = proces.pid;
        		final String[] nazwyPakietow= proces.pkgList;
        		
        		AlertDialog ad=new AlertDialog.Builder(parent.getContext()).create();
        		ad.setTitle("Mened�er zada�");
        		ad.setMessage("Czy zamkn�� proces "+((ProcesInfo)lista.getItemAtPosition(position)).nazwa+"?");
        		//alertDialog.setIcon(R.drawable.icon);
        		ad.setButton(Dialog.BUTTON_POSITIVE, "Tak", 
        				new DialogInterface.OnClickListener() 
        				{   
        		      		public void onClick(DialogInterface dialog, int which) 
        		      		{   
        		      			//tak mozna zabic tylko wlasny proces
        		      			android.os.Process.killProcess(pid);
        		      			if (pid!=android.os.Process.myPid()) Toast.makeText(getApplicationContext(), "Mo�na zabi� tylko w�asny proces!", Toast.LENGTH_LONG).show();
        		      			
        		      			/*
        		      			ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
        		      			for(String nazwaPakietu : nazwyPakietow)
        		      			{
        		      				am.restartPackage(nazwaPakietu); //deprecated
                                                        //am.killBackgroundProcesses(nazwaPakietu);
        		      			}
        		      			*/
        	        		
        		      			odswiezListe();        		      			        		      			
        		      			return;
        		      		} 
        		      	});    
        		ad.setButton(Dialog.BUTTON_NEGATIVE, "Nie",
        				new DialogInterface.OnClickListener() 
						{   
		      				public void onClick(DialogInterface dialog, int which) 
		      				{   
		      					return;   
		      				} 
						});
        		ad.show();        		
            }
        });
    }

* Aby dzia�a�o zamykanie zada� umieszczone w komentarzach nale�y do pliku manifestu doda� uprawnienie (za application):

     <uses-permission android:name="android.permission.RESTART_PACKAGES" />
 
  Ale uwaga:

     This constant was deprecated in API level 8.
     The restartPackage(String) API is no longer supported.

** Nowsze jest: am.killBackgroundProcesses(nazwaPakietu);

<uses-permission android:name="android.permission.KILL_BACKGROUND_PROCESSES" />

-------------------------------------------------------------

Zmiana nazwy bie��cej aktywno�ci:
1. W drzewie klas projektu (widok Android) odnajdujemy klas� MenedzerActivity
2. Z menu kontekstowego: Refactor, Rename lub Shift+F6
3. Zmieniamy nazwe na ProcesyActivity (zmieni si� r�wnie� nazwa pliku)

Dodawanie aktywno�ci do aplikacji
1. W drzewie projektu przejd� do pliku manifestu AndroidManifest.xml.
2. Z menu kontekstowego: New, Activity, Empty Activity.
3. W oknie New Android Activity
Activity name: ZadaniaActivity
Kliknij Finish

Powstanie nowy plik o zawarto�ci:

package pl.umk.fizyka.menedzerzadan;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

public class ZadaniaActivity extends AppCompatActivity {

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

Zmiana domy�lnej aktywno�ci:
1. W pliku manifestu wystarczy przenie�� element intent-filter do elementu nowej aktywno�ci.

        <activity android:name=".ProcesyActivity">
        </activity>
        <activity android:name=".ZadaniaActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

Uruchomiona zostanie pierwsza aktywno�� z odpowiedni� intencj�.

Mo�na r�wnie� zmieni� uruchamian� aktywno�� z poziomu Android Studio:
1. Z menu Run wybierz Edit Configurations...
2. W sekcji Launch Options, z rozwijanej listy Launch wybierz "Specified Activity".
3. W polu Activity ustaw ZadaniaActivity
Wskazana aktywno�� musi mie� intent-filter

Zmiana tytu�u:
Tytu� okna wy�wietlany w TitleBar jest ustawiony w manife�cie w elemencie application: android:label="@string/app_name"
Mo�na go nadpisa� na poziomie aktywno�ci: 

        <activity android:name=".ProcesyActivity">
        </activity>
        <activity 
            android:name=".ZadaniaActivity"
            android:label="Zadania:">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

Tytu� mo�na zmieni� te� z kodu: this.setTitle("Zadania:"); (w onCreate odpowiedniej aktywno�ci)



1. Zasadniczy interfejs nowej aktywno�ci b�dzie opiera� si� na tym samym layoucie 
   (tj. res/layout/activity_menedzer.xml). Uzyskamy to dodaj�c do metody onCreate
   polecenie:

setContentView(R.layout.activity_menedzer);

2. Zmienimy format wiersza. Do res/layout dodajmy kolejny plik typu 
   Android XML Layout File: wiersz_zadania.xml ze wskazaniem na LinearLayout.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:background="#FFFFFF"
    android:orientation="horizontal" >

    <ImageView
        android:id="@+id/ikona"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@mipmap/ic_launcher" />

    <LinearLayout
        android:id="@+id/linearLayout1"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical" >

        <TextView
            android:id="@+id/nazwa"
            android:textSize="18sp"
            android:textColor="#000000"
            android:text="Nazwa"
            android:layout_width="fill_parent"
            android:layout_height="24sp" />
        <TextView
            android:id="@+id/opis"
            android:textSize="10sp"
            android:textColor="#000000"
            android:text="Opis"
            android:layout_width="fill_parent"
            android:layout_height="14sp"
            android:maxLines="1"
            android:ellipsize="marquee" />

    </LinearLayout>

</LinearLayout>

3. W klasie ZadaniaActivity definiujemy metody pobierajace zadania:

package pl.umk.menedzerzadan;

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

import android.app.Activity;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningTaskInfo;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;

public class ZadaniaActivity extends Activity {

	List<RunningTaskInfo> listaZadan;
	ListView lista;	
	
	private List<RunningTaskInfo> pobierzListeZadan() 
    {
        ActivityManager am = (ActivityManager) this.getSystemService(ACTIVITY_SERVICE);
        return am.getRunningTasks(Integer.MAX_VALUE); //deprecated, wymaga uprawnienia GET_TASKS
    }	
	
	private class ZadanieInfo
	{
		public Drawable ikona;
		public String nazwa;
		public String opis;
	}
	
	private class ZadaniaAdapter extends ArrayAdapter<ZadanieInfo> 
	{
        private ArrayList<ZadanieInfo> items;

        public ZadaniaAdapter(Context context, int textViewResourceId, ArrayList<ZadanieInfo> items) 
        {
        	super(context, textViewResourceId, items);
            this.items = items;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) 
        {
        	View v = convertView;
            if (v == null) 
            {
            	LayoutInflater vi = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                v = vi.inflate(R.layout.wiersz_zadania, null);
            }
            ZadanieInfo o = items.get(position);
            if (o != null) 
            {
            	ImageView ikona = (ImageView)v.findViewById(R.id.ikona);
            	if (ikona != null) ikona.setImageDrawable(o.ikona);            	
            	
            	TextView nazwa = (TextView)v.findViewById(R.id.nazwa);
            	if (nazwa != null) nazwa.setText(o.nazwa);
            	
            	TextView opis = (TextView)v.findViewById(R.id.opis);                
                if(opis != null) opis.setText(o.opis);            	
            }
            return v;
        }
	}
	
	private void wypelnijListeNazwamiZadan(ListView lista)
	{
		PackageManager pm=this.getPackageManager();		
		
		int liczbaZadan=listaZadan.size();
		ArrayList<ZadanieInfo> opisyZadan=new ArrayList<ZadanieInfo>(liczbaZadan);
        for (int i = 0; i < liczbaZadan; i++) 
        {
        	try
        	{
        		RunningTaskInfo zadanie=listaZadan.get(i);
        		ZadanieInfo opisZadania=new ZadanieInfo();
        		ApplicationInfo ai=pm.getApplicationInfo(zadanie.baseActivity.getPackageName(), PackageManager.GET_META_DATA);        	
        		opisZadania.ikona=pm.getApplicationIcon(ai);
        		opisZadania.nazwa=pm.getApplicationLabel(ai).toString();
        		opisZadania.opis=Integer.toString(ai.uid)+", "+ai.packageName+", "+ai.sourceDir;
        		opisyZadan.add(opisZadania);        		        		
        	}
        	catch(Exception exc)
        	{
        	}
        	ZadaniaAdapter a=new ZadaniaAdapter(this,R.layout.wiersz_zadania,opisyZadan); 
    		lista.setAdapter(a);
        }
	}
	
	private void odswiezListe()
        {       
		listaZadan=pobierzListeZadan();
    	        lista.setAdapter(null);
    	        wypelnijListeNazwamiZadan(lista);
        }
	
	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle savedInstanceState) {
	    super.onCreate(savedInstanceState);
            //setContentView(R.layout.activity_zadania);	
	    setContentView(R.layout.activity_menedzer);
	}

}

4. Metoda ActivityManager.getRunningTasks wymaga uprawnienia GET_TASKS:
   a. przechodzimy do AndroidManifest.xml
   b. uzupe�niamy list� uprawnie�:

<uses-permission android:name="android.permission.RESTART_PACKAGES" />
<uses-permission android:name="android.permission.GET_TASKS" />

* W nowszych wersjach Androida dzia�a bez tego uprawnienia, a uprawnienie jest deprecated!

4. Nast�pnie do metody onCreate dodajemy kod, kt�ry wykorzysta powy�sze metody.
   I tym razem u�yjemy okna dialogowego AlertDialog.

	@Override
	public void onCreate(Bundle savedInstanceState) {
	    super.onCreate(savedInstanceState);
	
	    setContentView(R.layout.activity_menedzer);
	    
	    listaZadan=pobierzListeZadan();
	    lista=(ListView)findViewById(R.id.listView1);
        wypelnijListeNazwamiZadan(lista);
        lista.setOnItemClickListener(new AdapterView.OnItemClickListener() 
        {
        	public void onItemClick(AdapterView<?> parent, View view, int position, long id) 
        	{
        		RunningTaskInfo zadanie=listaZadan.get(position);
        		final String nazwaPakietu= zadanie.baseActivity.getPackageName();
        		
        		AlertDialog ad=new AlertDialog.Builder(parent.getContext()).create();
        		ad.setTitle("Mened�er zada�");
        		ad.setMessage("Czy zamkn�� zadanie "+((ZadanieInfo)lista.getItemAtPosition(position)).nazwa+"?");
        		//alertDialog.setIcon(R.drawable.icon);
        		ad.setButton(Dialog.BUTTON_POSITIVE, "Tak", 
        				new DialogInterface.OnClickListener() 
        				{   
        		      		public void onClick(DialogInterface dialog, int which) 
        		      		{   
        		      			ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE);        		      			
        		      			//am.restartPackage(nazwaPakietu);        		      				
                                                am.killBackgroundProcesses(nazwaPakietu);
        		    		
        		      			odswiezListe();        		      			        		      			
        		      			return;
        		      		} 
        		      	});    
        		ad.setButton(Dialog.BUTTON_NEGATIVE, "Nie",
        				new DialogInterface.OnClickListener() 
						{   
		      				public void onClick(DialogInterface dialog, int which) 
		      				{   
		      					return;   
		      				} 
						});
        		ad.show();        		
            }
        });
	}


************************************
W domu: post�puj�c podobnie przygotowa� aktywno�� UslugiActivity wy�wietlaj�c� list� us�ug.