יום שבת, 10 בנובמבר 2012

Android Http Widget



עבר הרבה זמן מהמאמר האחרון שלי בנושא Android, והגיע הזמן לרענן את הנושא, אחד הדברים שתמיד אהבתי זה את כל ה Widgets המגניבים שעל שולחן העבודה שהופכים אותו ליותר מעניין מאשר קבוצה של אייקונים סטטים מיושנים, הרעיון להפוך את שולחן העבודה הקר לאינטרקטיבי יותר שיזרים מידע באופן שוטף למשתמש, אבל חשוב לזכור ש Widget אומנם מתנהג כתוכנה לכל דבר אבל קיימות עליו מגבלויות תיכנותיות ועיצוביות, ולא הכל אפשרי לביצוע, אחד האתגרים ב Widget הוא שילוב עם האינטרנט שזה משהו לא טריוויאלי, בקיצור כאב ראש לא קטן.

אופי הפרויקט מאוד פשוט, כל כמה שניות ה Widget ניגש לאינטרנט ,מקבל את השעה של השרת המרוחק ומציג אותו למשתמש.

יצירת פרויקט חדש



שימו לב: הפרויקט מבוסס על API 10.


שימו לב: לבטל את ה Create Activity.



Widget Code

לאחר שנוצר הפרויקט נבנה את ה Class המרכזי עבור ה Widget.


 ה Class צריך לירוש את AppWidgetProvider:

package proxytype.widget.example;
import android.appwidget.AppWidgetProvider;

public class Main extends AppWidgetProvider {

}

צריך לעשות Override לפונקציה onUpdate, לחיצה על הכפתור הימני  Source -> Override/Implement Methods:



package proxytype.widget.example;

import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.Context;

public class Main extends AppWidgetProvider {

/* (non-Javadoc)
* @see android.appwidget.AppWidgetProvider#onUpdate(android.content.Context, android.appwidget.AppWidgetManager, int[])
*/
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
// TODO Auto-generated method stub
super.onUpdate(context, appWidgetManager, appWidgetIds);
}

}

אחד החסרונות הרציינים ב Widget ב Android הוא שלא ניתן להשתמש ב Webview, וזה מאוד מאכזב, Webview  היה מושלם למשימה ואני לא יודע מה בדיוק הסיבה שמפתחי גוגל מונעים מאיתנו לעבוד איתו , עדיין ניתן לבצע בקשות לאינטרנט אבל במקרה שנרצה לעדכן את ה Widget באופן קבוע צריכים ליצור Service שיבצע את הבקשה בכל פרק זמן מסויים ובסוף התהליך יעדכן את ה Widget, ניצור Class חדש שיורש מהמחלקה של Service.


package proxytype.widget.example;

import java.io.IOException;

import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;

import android.app.Service;
import android.appwidget.AppWidgetManager;
import android.content.ComponentName;
import android.content.Intent;
import android.os.IBinder;
import android.os.StrictMode;
import android.util.Log;
import android.widget.RemoteViews;

public class UpdateService extends Service {

/* (non-Javadoc)
* @see android.app.Service#onBind(android.content.Intent)
*/
@Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}

/* (non-Javadoc)
* @see android.app.Service#onCreate()
*/
@Override
public void onCreate() {
// TODO Auto-generated method stub
super.onCreate();

//chage thread policy running network inside the main
//thread, this is exmaple better use AsyncTask Class
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
       StrictMode.setThreadPolicy(policy);
}

/* (non-Javadoc)
* @see android.app.Service#onStartCommand(android.content.Intent, int, int)
*/
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// TODO Auto-generated method stub
update_widget();
return super.onStartCommand(intent, flags, startId);
}


private void update_widget()
{
//connecting to reomote Activity (widget main.xml)
                RemoteViews view = new RemoteViews(getPackageName(), R.layout.main);
     
               //update text box inside the widget
               view.setTextViewText(R.id.textView1, getPage());
     
              //update the widget with the new data
              ComponentName thisWidget = new ComponentName(this, Main.class);
              AppWidgetManager manager = AppWidgetManager.getInstance(this);
              manager.updateAppWidget(thisWidget, view);

}

//create the http call
private String getPage() {
    String str = "***";

        try
    {
    HttpClient hc = new DefaultHttpClient();
    HttpPost post = new HttpPost("http://192.168.1.8:443/default.aspx");
   
    //wating for response from request
    HttpResponse rp = hc.execute(post);
   
                //oblivious bad requests
    if(rp.getStatusLine().getStatusCode() == HttpStatus.SC_OK)
    {
    str = EntityUtils.toString(rp.getEntity());
    }
    }catch(IOException e){
    Log.e("ERROR", e.getMessage());
    }
   
    return str;
    }
}

נחזור ל Class שממנו הכל מתחיל ונכניס בו את השינויים הבאים, אנחנו צריכים שעון שיפעיל את ה Service בשביל לעדכן את ה Widget כל כמה שניות, נוכל לממש זאת בעזרת הספרייה AlarmManager.

package proxytype.widget.example;

import java.util.Calendar;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.Context;
import android.content.Intent;

public class Main extends AppWidgetProvider {

private PendingIntent service = null; 
/* (non-Javadoc)
* @see android.appwidget.AppWidgetProvider#onUpdate(android.content.Context, android.appwidget.AppWidgetManager, int[])
*/ 
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
// TODO Auto-generated method stub
super.onUpdate(context, appWidgetManager, appWidgetIds);
//system RTC to schedule mission
final AlarmManager alarmManager = (AlarmManager) 
               context.getSystemService(Context.ALARM_SERVICE);
//create instance to service
final Intent ServiceShow = new Intent(context, UpdateService.class);
//set the alarm clock to zero
final Calendar alarmTime = Calendar.getInstance();
alarmTime.set(Calendar.MINUTE, 0);
alarmTime.set(Calendar.SECOND, 0);
alarmTime.set(Calendar.MILLISECOND, 0);
        
        if (service == null)
        {
        //calling others application in this case to the service 
        //using the intent show to start the service,
        //and allow it to update this application
            service = PendingIntent.getService(context, 0, ServiceShow, 
                          PendingIntent.FLAG_CANCEL_CURRENT);
        }
        //set the RTC clock to update the widget every 3 seconds
        alarmManager.setRepeating(AlarmManager.RTC, alarmTime.getTime().getTime(), 3000, service);
}

/* (non-Javadoc)
* @see android.appwidget.AppWidgetProvider#onDeleted(android.content.Context, int[])
*/
@Override
public void onDeleted(Context context, int[] appWidgetIds) {
// TODO Auto-generated method stub
super.onDeleted(context, appWidgetIds);
}

/* (non-Javadoc)
* @see android.appwidget.AppWidgetProvider#onDisabled(android.content.Context)
*/
@Override
public void onDisabled(Context context) {
// TODO Auto-generated method stub
super.onDisabled(context);
 
final AlarmManager m = (AlarmManager) 
                context.getSystemService(Context.ALARM_SERVICE);  
               if (service != null)
             {
        //disable the clock
                 m.cancel(service);
             }
}

/* (non-Javadoc)
* @see android.appwidget.AppWidgetProvider#onEnabled(android.content.Context)
*/
@Override
public void onEnabled(Context context) {
// TODO Auto-generated method stub
super.onEnabled(context);
}

/* (non-Javadoc)
* @see android.appwidget.AppWidgetProvider#onReceive(android.content.Context, android.content.Intent)
*/
@Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
super.onReceive(context, intent);
}
}

Widget Setup

אחרי שהכנו את הקוד יש תהליך בירוקרטי עבור יצירת Widget, לכל Widget חייב להיות Provider על מנת לעבוד, ה Provider הוא קובץ הגדרות בשפת XML, שנמצא בתקיית Xml בפרויקט, חשוב לייצר תיקייה כזו במקרה שהיא לא קיימת, ניצור בספרייה קובץ בשם provider.xml.



נרשום בתוכו את השורות הבאות.

<?xml version="1.0" encoding="utf-8" ?>
<appwidget-provider
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:minWidth="146dp"
    android:initialLayout="@layout/main"
    android:updatePeriodMillis="1000"
    android:minHeight="144dp"/>



צריך להכניס לקובץ ה Manifest של התוכנית מספר שינויים, הראשון הוא להצהיר שהתוכנית היא Widget ולחבר לה את ה Provider ,אח"כ להוסיף את ה Service ולסיום טיפול בהרשאות :

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="proxytype.widget.example"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk android:minSdkVersion="10" />
     
    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name" >
        
        <receiver android:name=".Main" android:label="Server Clock">
            <intent-filter>
                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
            </intent-filter>
            <meta-data
                android:name="android.appwidget.provider"
                android:resource="@xml/provider" />
        </receiver>

        <service android:permission="android.permission.INTERNET" android:enabled="true" android:name=".UpdateService" />

    </application>
 <uses-permission
        android:name="android.permission.INTERNET"></uses-permission>
</manifest>


קובץ ה Layout של התוכנית main.xml:

<?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/textView1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="TextView" />

</LinearLayout>

תוצאה סופית:



סיכום:

Widget היא דרך מעניינת להזרים מידע באופן שוטף לשולחן העבודה של המשתמש, המידע חייב להיות מתומצת וה Widget צריך להיות פשוט, אם התוכנית שלכם מסובכת עדיף שתבנו אפליקציה ותחסכו לעצמכם הרבה כאב ראש.

למידע מקיף עבור Widgets ב Android לחץ כאן.

בהצלחה...

אין תגובות:

הוסף רשומת תגובה