-
Notifications
You must be signed in to change notification settings - Fork 158
Tutorial: make a widget
Make a widget that can be placed on the home screen. The widget should display some current information and respond to user interaction. It should be configurable at install time.
- You should have completed the Getting started with Ruboto tutorial.
- You should have read http://developer.android.com/guide/practices/ui_guidelines/widget_design.html
- You should have read http://developer.android.com/guide/topics/appwidgets/index.html
- You should have read http://developer.android.com/design/patterns/widgets.html
This tutorial has been tested with the following setups
Platform | JDK | ant | Ruby | ruboto | RubotoCore | Device | API level | Tester |
---|---|---|---|---|---|---|---|---|
OS X 10.8.2 | 1.7.0_09 | 1.8.2 | MRI 1.8.7 | 0.10.0.rc.1 | 0.4.9 | Samsung Galaxy S3 | android-15 | donv |
OS X 10.8.2 | 1.7.0_09 | 1.8.2 | MRI 1.8.7 | 0.10.0.rc.1 | 0.4.9 | Emulator | android-15 | donv |
Connect your device.
ruboto gen app --package org.ruboto.example.widget --target android-15
cd widget
rake update_scripts:restart
You should see the standard "What hath Matz wrought?" screen.
ruboto gen subclass android.appwidget.AppWidgetProvider --name WidgetProvider --method_base on
Add the following lines to your AndroidManifest.xml file, at the end of the application
tag:
<receiver android:name="WidgetProvider" >
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data android:name="android.appwidget.provider" android:resource="@xml/widget_info" />
</receiver>
res/xml/widget_info.xml
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:minWidth="294dp"
android:minHeight="72dp"
android:updatePeriodMillis="86400000"
android:previewImage="@drawable/ic_launcher"
android:initialLayout="@layout/widget"
android:configure="org.ruboto.example.widget.WidgetConfigure"
android:resizeMode="horizontal|vertical"
/>
Edit the file src/org/ruboto/example/widget/WidgetProvider.java. Change the generated code for onReceive.
Remove this line near the top of the file:
ScriptLoader.loadScript(this);
and edit the onReceive
method.
Change
if (!JRubyAdapter.isInitialized()) {
Log.i("Method called before JRuby runtime was initialized: WidgetProvider#onReceive");
{super.onReceive(context, intent); return;}
}
to this:
if (!JRubyAdapter.setUpJRuby(context)) {
Log.e("Unable to initialize the JRuby runtime.");
{super.onReceive(context, intent); return;}
}
ScriptLoader.loadScript(this);
Edit the src/widget_provider.rb file to this:
require 'ruboto/base'
require 'ruboto/package'
require 'widget_configure'
import android.appwidget.AppWidgetManager
import android.appwidget.AppWidgetProvider
import android.content.ComponentName
import android.content.Context
import android.content.pm.PackageManager
import android.os.SystemClock
import android.util.Log
import android.widget.RemoteViews
class WidgetProvider
import org.ruboto.example.widget.R
TAG = 'WidgetProvider'
def onUpdate(context, appWidgetManager, appWidgetIds)
Log.d(TAG, "onUpdate")
appWidgetIds.each do |appWidgetId|
titlePrefix = WidgetConfigure.loadTitlePref(context, appWidgetId)
WidgetProvider.updateAppWidget(context, appWidgetManager, appWidgetId, titlePrefix)
end
end
def onDeleted(context, appWidgetIds)
Log.d(TAG, "onDeleted")
appWidgetIds.each do |appWidgetId|
WidgetConfigure.deleteTitlePref(context, appWidgetId)
end
end
def onEnabled(context)
Log.d(TAG, "onEnabled")
end
def onDisabled(context)
Log.d(TAG, "onDisabled")
PackageManager pm = context.getPackageManager()
end
def self.updateAppWidget(context, appWidgetManager, appWidgetId, titlePrefix)
Log.d(TAG, "updateAppWidget appWidgetId=#{appWidgetId} titlePrefix=#{titlePrefix}")
text = context.getString(R.string.widget_text_format, WidgetConfigure.loadTitlePref(context, appWidgetId))
views = RemoteViews.new(context.getPackageName(), R.layout.widget)
views.setTextViewText(Ruboto::Id.text, text)
appWidgetManager.updateAppWidget(appWidgetId, views)
end
end
Add a new file res/layout/widget.xml
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/text"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#88ffffff"
android:textColor="#ffff0000"
android:text="Hullo!"
android:gravity="center"
android:textSize="36pt"
/>
Edit res/values/strings.xml and add the following line inside the resources
tag:
<string xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2" name="widget_text_format"><xliff:g id="prefix">%1$s</xliff:g></string>
ruboto gen class Activity --name WidgetConfigure
Edit the AndroidManifest.xml and change
<activity android:name='WidgetConfigure'/>
to this
<activity android:name="WidgetConfigure">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE"/>
</intent-filter>
</activity>
This will fire the WidgetConfigure activity when a new widget is created. It will allow us to set parameters for each widget instance.
Edit src/widget_configure.rb to this:
require 'ruboto/widget'
import android.app.Activity
import android.appwidget.AppWidgetManager
import android.content.Context
import android.content.Intent
import android.content.SharedPreferences
import android.os.Bundle
import android.view.View
import android.widget.EditText
import java.util.ArrayList
import org.ruboto.example.widget.R
ruboto_import_widgets :Button, :EditText, :LinearLayout, :TextView
class WidgetConfigure
TAG = "WidgetConfigure"
PREFS_NAME = "org.ruboto.example.widget.WidgetProvider"
PREF_PREFIX_KEY = "prefix_"
def on_create(bundle)
super
set_title 'Ruboto Widget Configuration'
@@mAppWidgetId = AppWidgetManager::INVALID_APPWIDGET_ID
setResult(RESULT_CANCELED)
self.content_view =
linear_layout :orientation => :vertical do
@text_view = text_view :text => 'Configure me!', :id => 42,
:layout => {:width => :match_parent},
:gravity => :center, :text_size => 48.0
@mAppWidgetPrefix = edit_text :id => 43, :layout => {:width => :match_parent}
button :text => 'Save', :layout => {:width => :match_parent},
:id => 44, :on_click_listener => proc {|v| configure(v) }
end
intent = getIntent()
extras = intent.getExtras()
if (extras != nil)
@mAppWidgetId = extras.getInt(
AppWidgetManager::EXTRA_APPWIDGET_ID, AppWidgetManager::INVALID_APPWIDGET_ID)
end
if (@mAppWidgetId == AppWidgetManager::INVALID_APPWIDGET_ID)
finish()
end
@mAppWidgetPrefix.setText(WidgetConfigure.loadTitlePref(self, @mAppWidgetId))
rescue Exception
puts "Exception creating activity: #{$!}"
puts $!.backtrace.join("\n")
end
def configure(v)
context = self
titlePrefix = @mAppWidgetPrefix.getText().toString()
WidgetConfigure.saveTitlePref(context, @mAppWidgetId, titlePrefix)
appWidgetManager = AppWidgetManager.getInstance(context)
WidgetProvider.updateAppWidget(context, appWidgetManager, @mAppWidgetId, titlePrefix)
resultValue = Intent.new()
resultValue.putExtra(AppWidgetManager::EXTRA_APPWIDGET_ID, @mAppWidgetId.to_java(:int))
setResult(RESULT_OK, resultValue)
finish()
end
def self.saveTitlePref(context, appWidgetId, text)
prefs = context.getSharedPreferences(PREFS_NAME, 0).edit()
prefs.putString("#{PREF_PREFIX_KEY}#{appWidgetId}", text)
prefs.commit()
end
def self.loadTitlePref(context, appWidgetId)
prefs = context.getSharedPreferences(PREFS_NAME, 0)
prefix = prefs.getString("#{PREF_PREFIX_KEY}#{appWidgetId}", nil)
if (prefix != nil)
return prefix
else
return "Hullo!"
end
end
def self.deleteTitlePref(context, appWidgetId)
end
def self.loadAllTitlePrefs(context, appWidgetIds, texts)
end
end
Edit *src/widget_activity.rb to this:
require 'ruboto/widget'
java_import android.content.pm.PackageManager
ruboto_import_widgets :LinearLayout, :TextView
class WidgetActivity
def on_create(bundle)
super
set_title 'Ruboto Flashlight'
flash_available = package_manager.hasSystemFeature(PackageManager::FEATURE_CAMERA_FLASH)
self.content_view =
linear_layout :orientation => :vertical do
@text_view = text_view :text => flash_available ? 'Place the Flashlight widget on your home screen.' : 'No flash available.',
:id => 42, :layout => {:width => :match_parent},
:gravity => :center, :text_size => 48.0
end
rescue
puts "Exception creating activity: #{$!}"
puts $!.backtrace.join("\n")
end
end
rake install
Navigate to your app list, and select to see the widgets. You should find a widget called "Widget". Long-click it, and then drag it to a free space on your home screen. The configuration activity will pop up. Set a custom display text and click "Save". The text you wrote will appear in the widget on the home screen.
Congratulations!