added Android examples

git-svn-id: http://lampsvn.epfl.ch/svn-repos/scala/scala/trunk@13536 5e8d7ff9-d8ef-0310-90f0-a4852d11357a
This commit is contained in:
michelou 2007-12-13 18:16:14 +00:00
parent 2121df331c
commit d6e1be5796
16 changed files with 1401 additions and 1 deletions

View File

@ -916,7 +916,6 @@ ANDROID
basedir="${android.dir}/lib/library" basedir="${android.dir}/lib/library"
includes="scala/**/*.class" includes="scala/**/*.class"
/> />
<!-- examples ? -->
</target> </target>
<target name="android" depends="init"> <target name="android" depends="init">
@ -1827,6 +1826,11 @@ GENERATES A DISTRIBUTION
</quicksbaz> </quicksbaz>
<!-- Creates the Android package --> <!-- Creates the Android package -->
<if><isset property="android.supported"/><then> <if><isset property="android.supported"/><then>
<copy todir="${dist.current.dir}/doc/scala-android/examples">
<fileset dir="${docs.dir}/android-examples">
<exclude name="**/R.java"/>
</fileset>
</copy>
<quicksbaz <quicksbaz
file="${dist.dir}/scala-android-${version.number}.sbp" file="${dist.dir}/scala-android-${version.number}.sbp"
adfile="${dist.dir}/scala-android-${version.number}.advert" adfile="${dist.dir}/scala-android-${version.number}.advert"
@ -1835,6 +1839,7 @@ GENERATES A DISTRIBUTION
desc="The Scala Android package contains everything needed to use Scala on Android." desc="The Scala Android package contains everything needed to use Scala on Android."
link="${sbaz.universe}/scala-android-${version.number}.sbp"> link="${sbaz.universe}/scala-android-${version.number}.sbp">
<libset dir="${android.dir}/lib" includes="*.jar"/> <libset dir="${android.dir}/lib" includes="*.jar"/>
<docset dir="${dist.current.dir}/doc/scala-android" includes="**"/>
</quicksbaz> </quicksbaz>
</then></if> </then></if>
</target> </target>

View File

@ -0,0 +1,91 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
* Copyright (C) 2007 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.google.android.notepad">
<application android:icon="@drawable/app_notes"
android:label="@string/app_name">
<provider class="NotePadProvider"
android:authorities="com.google.provider.NotePad" />
<activity class="NotesList" android:label="@string/title_notes_list">
<intent-filter>
<action android:value="android.intent.action.MAIN" />
<category android:value="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:value="android.intent.action.VIEW" />
<action android:value="android.intent.action.EDIT" />
<action android:value="android.intent.action.PICK" />
<category android:value="android.intent.category.DEFAULT" />
<type android:value="vnd.android.cursor.dir/vnd.google.note" />
</intent-filter>
<intent-filter>
<action android:value="android.intent.action.GET_CONTENT" />
<category android:value="android.intent.category.DEFAULT" />
<type android:value="vnd.android.cursor.item/vnd.google.note" />
</intent-filter>
</activity>
<activity class="NoteEditor" android:label="@string/title_note">
<!-- This filter says that we can view or edit the data of
a single note -->
<intent-filter android:label="@string/resolve_edit">
<action android:value="android.intent.action.VIEW" />
<action android:value="android.intent.action.EDIT" />
<action android:value="com.google.android.notepad.action.EDIT_NOTE" />
<category android:value="android.intent.category.DEFAULT" />
<type android:value="vnd.android.cursor.item/vnd.google.note" />
</intent-filter>
<!-- This filter says that we can create a new note inside
of a directory of notes. -->
<intent-filter>
<action android:value="android.intent.action.INSERT" />
<category android:value="android.intent.category.DEFAULT" />
<type android:value="vnd.android.cursor.dir/vnd.google.note" />
</intent-filter>
</activity>
<activity class="TitleEditor" android:label="@string/title_edit_title"
android:theme="@android:style/Theme.Dialog">
<!-- This activity implements an alternative action that can be
performed on notes: editing their title. It can be used as
a default operation if the user invokes this action, and is
available as an alternative action for any note data. -->
<intent-filter android:label="@string/resolve_title">
<!-- This is the action we perform. It is a custom action we
define for our application, not a generic VIEW or EDIT
action since we are not a general note viewer/editor. -->
<action android:value="com.google.android.notepad.action.EDIT_TITLE" />
<!-- DEFAULT: execute if being directly invoked. -->
<category android:value="android.intent.category.DEFAULT" />
<!-- ALTERNATIVE: show as an alternative action when the user is
working with this type of data. -->
<category android:value="android.intent.category.ALTERNATIVE" />
<!-- SELECTED_ALTERNATIVE: show as an alternative action the user
can perform when selecting this type of data. -->
<category android:value="android.intent.category.SELECTED_ALTERNATIVE" />
<!-- This is the data type we operate on. -->
<type android:value="vnd.android.cursor.item/vnd.google.note" />
</intent-filter>
</activity>
</application>
</manifest>

View File

@ -0,0 +1,222 @@
<?xml version="1.0" ?>
<project name="NotePad" default="package">
<property name="sdk-folder" value="/home/linuxsoft/apps/android-m3-rc22a" />
<property name="android-tools" value="/home/linuxsoft/apps/android-m3-rc22a/tools" />
<!-- The intermediates directory -->
<!-- Eclipse uses "bin" for its own output, so we do the same. -->
<property name="outdir" value="bin" />
<!-- No user servicable parts below. -->
<!-- Input directories -->
<property name="resource-dir" value="res" />
<property name="asset-dir" value="assets" />
<property name="srcdir" value="src" />
<!-- Output directories -->
<property name="outdir-classes" value="${outdir}/classes" />
<!-- Create R.java in the source directory -->
<property name="outdir-r" value="src" />
<!-- Intermediate files -->
<property name="dex-file" value="classes.dex" />
<property name="intermediate-dex" value="${outdir}/${dex-file}" />
<!-- The final package file to generate -->
<property name="out-package" value="${outdir}/${ant.project.name}.apk" />
<!-- Tools -->
<property name="aapt" value="${android-tools}/aapt" />
<property name="aidl" value="${android-tools}/aidl" />
<property name="dx" value="${android-tools}/dx" />
<property name="zip" value="zip" />
<property name="android-jar" value="${sdk-folder}/android.jar" />
<!-- Scala -->
<property environment="env"/>
<property name="scala.dir" value="${env.SCALA_HOME}"/>
<property name="scala-compiler.jar" value="${scala.dir}/lib/scala-compiler.jar"/>
<property name="scala-library.jar" value="${scala.dir}/lib/scala-library.jar"/>
<property name="scala-android.jar" value="${scala.dir}/lib/scala-android.jar"/>
<fail message="Missing library scala-android.jar (use sbaz to install it)">
<condition><not><available file="${scala-android.jar}"/></not></condition>
</fail>
<property name="scala-depend.jar" value="${android-tools}/lib/scala-depend.jar"/>
<path id="scala.path">
<pathelement path="${scala-library.jar}"/>
<pathelement path="${scala-compiler.jar}"/>
</path>
<path id="scalac.path">
<pathelement path="${android-jar}"/>
<pathelement path="${scala-library.jar}"/>
</path>
<taskdef
resource="scala/tools/ant/antlib.xml"
classpathref="scala.path"
/>
<macrodef name="smartjar">
<attribute name="srcdir"/>
<attribute name="basedir"/>
<attribute name="classname"/>
<attribute name="destfile"/>
<sequential>
<depend
srcdir="@{srcdir}"
destdir="@{basedir}" closure="true"
cache="@{basedir}"
classpath="${android-jar}"
/>
<java
classname="ch.epfl.lamp.util.depend" output="@{basedir}/classes.dep"
classpath="${scala-library.jar}${path.separator}${scala-depend.jar}">
<arg line="@{basedir}${file.separator}dependencies.txt"/>
<arg line="@{classname}"/>
</java>
<jar
destfile="@{destfile}"
basedir="@{basedir}"
includesfile="@{basedir}/classes.dep"
/>
</sequential>
</macrodef>
<!-- Rules -->
<!-- Create the output directories if they don't exist yet. -->
<target name="dirs">
<mkdir dir="${outdir}" />
<mkdir dir="${outdir-classes}" />
</target>
<!-- Generate the R.java file for this project's resources. -->
<target name="resource-src" depends="dirs">
<echo>Generating R.java...</echo>
<exec executable="${aapt}" failonerror="true">
<arg value="compile" />
<arg value="-m" />
<arg value="-J" />
<arg value="${outdir-r}" />
<arg value="-M" />
<arg value="AndroidManifest.xml" />
<arg value="-S" />
<arg value="${resource-dir}" />
<arg value="-I" />
<arg value="${android-jar}" />
</exec>
</target>
<!-- Generate java classes from .aidl files. -->
<target name="aidl" depends="dirs">
<apply executable="${aidl}" failonerror="true">
<fileset dir="${srcdir}">
<include name="**/*.aidl"/>
</fileset>
</apply>
</target>
<!-- Compile this project's .java files into .class files. -->
<target name="compile" depends="dirs, resource-src, aidl">
<javac encoding="ascii" target="1.5" debug="true" extdirs=""
srcdir="."
destdir="${outdir-classes}"
bootclasspath="${android-jar}" />
<scalac encoding="ascii" target="jvm-1.5"
srcdir="${srcdir}"
destdir="${outdir-classes}"
bootclasspathref="scalac.path" />
<unjar src="${scala-android.jar}" dest="${outdir-classes}"/>
<smartjar
srcdir="${srcdir}"
basedir="${outdir-classes}"
classname="com.google.android.notepad.NotePad"
destfile="${outdir}/${ant.project.name}.jar"
/>
<delete includeemptydirs="true">
<fileset dir="${outdir-classes}" includes="**/*"/>
</delete>
<unjar src="${outdir}/${ant.project.name}.jar" dest="${outdir-classes}"/>
</target>
<!-- Convert this project's .class files into .dex files. -->
<target name="dex" depends="compile">
<exec executable="${dx}" failonerror="true">
<arg value="-JXmx384M" />
<arg value="--dex" />
<arg value="--output=${intermediate-dex}" />
<arg value="--locals=full" />
<arg value="--positions=lines" />
<arg path="${outdir-classes}" />
</exec>
</target>
<!-- Put the project's resources into the output package file. -->
<target name="package-res-and-assets">
<echo>Packaging resources and assets...</echo>
<exec executable="${aapt}" failonerror="true">
<arg value="package" />
<arg value="-f" />
<arg value="-c" />
<arg value="-M" />
<arg value="AndroidManifest.xml" />
<arg value="-S" />
<arg value="${resource-dir}" />
<arg value="-A" />
<arg value="${asset-dir}" />
<arg value="-I" />
<arg value="${android-jar}" />
<arg value="${out-package}" />
</exec>
</target>
<!-- Same as package-res-and-assets, but without "-A ${asset-dir}" -->
<target name="package-res-no-assets">
<echo>Packaging resources...</echo>
<exec executable="${aapt}" failonerror="true">
<arg value="package" />
<arg value="-f" />
<arg value="-c" />
<arg value="-M" />
<arg value="AndroidManifest.xml" />
<arg value="-S" />
<arg value="${resource-dir}" />
<!-- No assets directory -->
<arg value="-I" />
<arg value="${android-jar}" />
<arg value="${out-package}" />
</exec>
</target>
<!-- Invoke the proper target depending on whether or not
an assets directory is present. -->
<!-- TODO: find a nicer way to include the "-A ${asset-dir}" argument
only when the assets dir exists. -->
<target name="package-res">
<available file="${asset-dir}" type="dir"
property="res-target" value="and-assets" />
<property name="res-target" value="no-assets" />
<antcall target="package-res-${res-target}" />
</target>
<!-- Put the project's .class files into the output package file. -->
<target name="package-java" depends="compile, package-res">
<echo>Packaging java...</echo>
<jar destfile="${out-package}"
basedir="${outdir-classes}"
update="true" />
</target>
<!-- Put the project's .dex files into the output package file. -->
<target name="package-dex" depends="dex, package-res">
<echo>Packaging dex...</echo>
<exec executable="${zip}" failonerror="true">
<arg value="-qj" />
<arg value="${out-package}" />
<arg value="${intermediate-dex}" />
</exec>
</target>
<!-- Create the package file for this project from the sources. -->
<target name="package" depends="package-dex" />
</project>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
* Copyright (C) 2007 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
-->
<view xmlns:android="http://schemas.android.com/apk/res/android" class="com.google.android.notepad.NoteEditor$MyEditText" id="@+id/note"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#ffffff"
android:padding="10dip"
android:scrollbars="vertical"
android:fadingEdge="vertical" />

View File

@ -0,0 +1,42 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
* Copyright (C) 2007 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingLeft="6dip"
android:paddingRight="6dip"
android:paddingBottom="3dip">
<EditText id="@+id/title"
android:maxLines="1"
android:layout_marginTop="2dip"
android:layout_width="wrap_content"
android:ems="25"
android:layout_height="wrap_content"
android:autoText="true"
android:capitalize="sentences"
android:scrollHorizontally="true" />
<Button id="@+id/ok"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right"
android:text="@string/button_ok" />
</LinearLayout>

View File

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
* Copyright (C) 2007 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
-->
<resources>
<string name="menu_delete">Ðéļéţé</string>
<string name="menu_insert">Ådd ñöţé</string>
<string name="menu_revert">®évérţ</string>
<string name="menu_discard">Ðîšçård</string>
<string name="resolve_edit">Édîţ ñöţé</string>
<string name="resolve_title">Édîţ ţîţļé</string>
<string name="title_create">Çréåţé ñöţé</string>
<string name="title_edit">Édîţ ñöţé</string>
<string name="title_notes_list">Ñöţé þåd</string>
<string name="title_note">Ñöţé</string>
<string name="title_edit_title">Ñöţé ţîţļé:</string>
<string name="app_name">Ñöţé Þåd</string>
<string name="button_ok">ÖĶ</string>
<string name="error_title">Érrör</string>
<string name="error_message">Érrör löading nöté</string>
</resources>

View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
* Copyright (C) 2007 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
-->
<resources>
<drawable name="even_stripe">#e5edfa</drawable>
<drawable name="odd_stripe">#ffffffff</drawable>
</resources>

View File

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
* Copyright (C) 2007 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
-->
<resources>
<string name="menu_delete">Delete</string>
<string name="menu_insert">Add note</string>
<string name="menu_revert">Revert</string>
<string name="menu_discard">Discard</string>
<string name="resolve_edit">Edit note</string>
<string name="resolve_title">Edit title</string>
<string name="title_create">Create note</string>
<string name="title_edit">Edit note</string>
<string name="title_notes_list">Note pad</string>
<string name="title_note">Note</string>
<string name="title_edit_title">Note title:</string>
<string name="app_name">Note Pad</string>
<string name="button_ok">OK</string>
<string name="error_title">Error</string>
<string name="error_message">Error loading note</string>
</resources>

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

View File

@ -0,0 +1,328 @@
/*
* Copyright (C) 2007 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.notepad
import com.google.provider.NotePad
import _root_.android.app.Activity
import _root_.android.app.Activity._
import _root_.android.content.{ComponentName, Context, Intent}
import _root_.android.database.Cursor
import _root_.android.graphics.{Canvas, Paint, Rect}
import _root_.android.net.ContentURI
import _root_.android.os.Bundle
import _root_.android.text.TextUtils
import _root_.android.util.{AttributeSet, Log, Config}
import _root_.android.view.{KeyEvent, Menu}
import _root_.android.widget.EditText
import java.util.Map
/**
* A generic activity for editing a note in a database. This can be used
* either to simply view a note (Intent.VIEW_ACTION), view and edit a note
* (Intent.EDIT_ACTION), or create a new note (Intent.INSERT_ACTION).
*/
object NoteEditor {
private val TAG = "Notes"
private val NOTE_INDEX = 1
private val TITLE_INDEX = 2
private val MODIFIED_INDEX = 3
/**
* Standard projection for the interesting columns of a normal note.
*/
private val PROJECTION = Array(
NotePad.Notes._ID, // 0
NotePad.Notes.NOTE, // 1
NotePad.Notes.TITLE, // 2
NotePad.Notes.MODIFIED_DATE // 3
)
// This is our state data that is stored when freezing.
private val ORIGINAL_CONTENT = "origContent"
// Identifiers for our menu items.
private val REVERT_ID = Menu.FIRST
private val DISCARD_ID = Menu.FIRST + 1
private val DELETE_ID = Menu.FIRST + 2
// The different distinct states the activity can be run in.
private val STATE_UNDEF = -1
private val STATE_EDIT = 0
private val STATE_INSERT = 1
// we need this constructor for ViewInflate
class MyEditText(context: Context, attrs: AttributeSet, params: Map)
extends EditText(context, attrs, params) {
private val mRect = new Rect()
private val mPaint = new Paint()
mPaint setStyle Paint.Style.STROKE
mPaint setColor 0xFF0000FF
override protected def onDraw(canvas: Canvas) {
val count = getLineCount()
val r = mRect
val paint = mPaint
for (i <- 0 until count) {
val baseline = getLineBounds(i, r)
canvas.drawLine(r.left, baseline + 1, r.right, baseline + 1, paint)
}
super.onDraw(canvas)
}
}
}
class NoteEditor extends Activity {
import NoteEditor._ // companion object
private var mState = STATE_UNDEF
private var mNoteOnly = false
private var mURI: ContentURI = _
private var mCursor: Cursor = null
private var mText: EditText = null
private var mOriginalContent: String = ""
override protected def onCreate(icicle: Bundle) {
super.onCreate(icicle)
val intent = getIntent()
//val type = intent.resolveType(this)
// Do some setup based on the action being performed.
val action = intent.getAction()
if (action equals Intent.EDIT_ACTION) {
// Requested to edit: set that state, and the data being edited.
mState = STATE_EDIT
mURI = intent.getData()
} else if (action equals Intent.INSERT_ACTION) {
// Requested to insert: set that state, and create a new entry
// in the container.
mState = STATE_INSERT
mURI = getContentResolver().insert(intent.getData(), null)
// If we were unable to create a new note, then just finish
// this activity. A RESULT_CANCELED will be sent back to the
// original activity if they requested a result.
if (mURI == null) {
Log.e("Notes", "Failed to insert new note into " +
getIntent().getData())
finish()
return
}
// The new entry was created, so assume all will end well and
// set the result to be returned.
setResult(RESULT_OK, mURI.toString())
} else {
// Whoops, unknown action! Bail.
Log.e(TAG, "Unknown action, exiting")
finish()
return
}
// Set the layout for this activity. You can find it
// in res/layout/hello_activity.xml
setContentView(R.layout.note_editor)
// The text view for our note, identified by its ID in the XML file.
mText = findViewById(R.id.note).asInstanceOf[EditText]
// Get the note!
mCursor = managedQuery(mURI, PROJECTION, null, null)
// If an instance of this activity had previously stopped, we can
// get the original text it started with.
if (icicle != null)
mOriginalContent = icicle getString ORIGINAL_CONTENT
}
override protected def onResume() {
super.onResume()
// If we didn't have any trouble retrieving the data, it is now
// time to get at the stuff.
if (mCursor != null) {
// Make sure we are at the one and only row in the cursor.
mCursor.first()
// Modify our overall title depending on the mode we are running in.
if (mState == STATE_EDIT)
setTitle(getText(R.string.title_edit))
else if (mState == STATE_INSERT)
setTitle(getText(R.string.title_create))
// This is a little nasty: we be resumed after previously being
// paused/stopped. We want to re-retrieve the data to make sure
// we are still accurately showing what is in the cursor... but
// we don't want to lose any UI state like the current cursor
// position. This trick accomplishes that. In the future we
// should have a better API for doing this...
val curState = mText.saveState()
val note = mCursor getString NOTE_INDEX
mText setText note
mText restoreState curState
// If we hadn't previously retrieved the original text, do so
// now. This allows the user to revert their changes.
if (mOriginalContent == null)
mOriginalContent = note
} else {
setTitle(getText(R.string.error_title))
mText setText getText(R.string.error_message)
}
}
override protected def onFreeze(outState: Bundle) {
// Save away the original text, so we still have it if the activity
// needs to be killed while paused.
outState.putString(ORIGINAL_CONTENT, mOriginalContent)
}
override protected def onPause() {
super.onPause()
// The user is going somewhere else, so make sure their current
// changes are safely saved away in the provider. We don't need
// to do this if only editing.
if (mCursor != null) {
val text = mText.getText().toString()
val length = text.length()
// If this activity is finished, and there is no text, then we
// do something a little special: simply delete the note entry.
// Note that we do this both for editing and inserting... it
// would be reasonable to only do it when inserting.
if (isFinishing() && (length == 0) && !mNoteOnly) {
setResult(RESULT_CANCELED)
deleteNote()
// Get out updates into the provider.
} else {
// This stuff is only done when working with a full-fledged note.
if (!mNoteOnly) {
// Bump the modification time to now.
mCursor.updateLong(MODIFIED_INDEX, System.currentTimeMillis())
// If we are creating a new note, then we want to also create
// an initial title for it.
if (mState == STATE_INSERT) {
var title = text.substring(0, Math.min(30, length))
if (length > 30) {
val lastSpace = title lastIndexOf ' '
if (lastSpace > 0)
title = title.substring(0, lastSpace)
}
mCursor.updateString(TITLE_INDEX, title)
}
}
// Write our text back into the provider.
mCursor.updateString(NOTE_INDEX, text)
// Commit all of our changes to persistent storage. Note the
// use of managedCommitUpdates() instead of
// mCursor.commitUpdates() -- this lets Activity take care of
// requerying the new data if needed.
managedCommitUpdates(mCursor)
}
}
}
override def onCreateOptionsMenu(menu: Menu): Boolean = {
super.onCreateOptionsMenu(menu)
// Build the menus that are shown when editing.
if (mState == STATE_EDIT) {
menu.add(0, REVERT_ID, R.string.menu_revert).setShortcut(
KeyEvent.KEYCODE_0, 0, KeyEvent.KEYCODE_R)
if (!mNoteOnly)
menu.add(0, DELETE_ID, R.string.menu_delete).setShortcut(
KeyEvent.KEYCODE_1, 0, KeyEvent.KEYCODE_D)
// Build the menus that are shown when inserting.
} else
menu.add(0, DISCARD_ID, R.string.menu_discard).setShortcut(
KeyEvent.KEYCODE_0, 0, KeyEvent.KEYCODE_D)
// If we are working on a real honest-to-ghod note, then append to the
// menu items for any other activities that can do stuff with it
// as well. This does a query on the system for any activities that
// implement the ALTERNATIVE_ACTION for our data, adding a menu item
// for each one that is found.
if (!mNoteOnly) {
val intent = new Intent(null, getIntent().getData())
intent addCategory Intent.ALTERNATIVE_CATEGORY
menu.addIntentOptions(
Menu.ALTERNATIVE, 0,
new ComponentName(this, classOf[NoteEditor]), null,
intent, 0, null)
}
true
}
override def onOptionsItemSelected(item: Menu.Item): Boolean = {
// Handle all of the possible menu actions.
item.getId() match {
case DELETE_ID =>
deleteNote()
finish()
case DISCARD_ID =>
cancelNote()
case REVERT_ID =>
cancelNote()
case _ =>
}
super.onOptionsItemSelected(item)
}
/**
* Take care of cancelling work on a note. Deletes the note if we
* had created it, otherwise reverts to the original text.
*/
private final def cancelNote() {
if (mCursor != null) {
if (mState == STATE_EDIT) {
mCursor.updateString(NOTE_INDEX, mOriginalContent)
mCursor.commitUpdates()
mCursor.deactivate()
mCursor = null
} else if (mState == STATE_INSERT)
deleteNote()
}
setResult(RESULT_CANCELED)
finish()
}
/**
* Take care of deleting a note. Simply deletes the entry.
*/
private final def deleteNote() {
if (mCursor != null) {
mText setText ""
mCursor.deleteRow()
mCursor.deactivate()
mCursor = null
}
}
}

View File

@ -0,0 +1,201 @@
/*
* Copyright (C) 2007 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.notepad
import com.google.provider.NotePad
import _root_.android.content.{ContentProvider, ContentProviderDatabaseHelper,
ContentURIParser, ContentValues, QueryBuilder,
Resources}
import _root_.android.database.{Cursor, SQLException}
import _root_.android.database.sqlite.SQLiteDatabase
import _root_.android.net.ContentURI
import _root_.android.text.TextUtils
import _root_.android.util.Log
import java.util.HashMap // note: setProjectionMap expects a Java map
/**
* Provides access to a database of notes. Each note has a title, the note
* itself, a creation date and a modified data.
*
*/
object NotePadProvider {
private val TAG = "NotePadProvider"
private val DATABASE_NAME = "note_pad.db"
private val DATABASE_VERSION = 2
private val NOTES_LIST_PROJECTION_MAP = new HashMap/*[String, String]*/()
NOTES_LIST_PROJECTION_MAP.put(NotePad.Notes._ID, "_id")
NOTES_LIST_PROJECTION_MAP.put(NotePad.Notes.TITLE, "title")
NOTES_LIST_PROJECTION_MAP.put(NotePad.Notes.NOTE, "note")
NOTES_LIST_PROJECTION_MAP.put(NotePad.Notes.CREATED_DATE, "created")
NOTES_LIST_PROJECTION_MAP.put(NotePad.Notes.MODIFIED_DATE, "modified")
private val NOTES = 1
private val NOTE_ID = 2
private val URL_MATCHER = new ContentURIParser(ContentURIParser.NO_MATCH)
URL_MATCHER.addURI("com.google.provider.NotePad", "notes", NOTES)
URL_MATCHER.addURI("com.google.provider.NotePad", "notes/#", NOTE_ID)
private class DatabaseHelper extends ContentProviderDatabaseHelper {
override def onCreate(db: SQLiteDatabase) {
db execSQL "CREATE TABLE notes (_id INTEGER PRIMARY KEY," +
"title TEXT," + "note TEXT," + "created INTEGER," +
"modified INTEGER" + ");"
}
override def onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
Log.w(TAG, "Upgrading database from version " + oldVersion + " to " +
newVersion + ", which will destroy all old data")
db execSQL "DROP TABLE IF EXISTS notes"
onCreate(db)
}
}
}
class NotePadProvider extends ContentProvider {
import NotePadProvider._
private var mDB: SQLiteDatabase = _
override def onCreate(): Boolean = {
val dbHelper = new DatabaseHelper()
mDB = dbHelper.openDatabase(getContext(), DATABASE_NAME, null, DATABASE_VERSION)
mDB != null
}
override def query(url: ContentURI, projection: Array[String],
selection: String, selectionArgs: Array[String],
groupBy: String, having: String, sort: String): Cursor = {
val qb = new QueryBuilder()
(URL_MATCHER `match` url) match {
case NOTES =>
qb setTables "notes"
qb setProjectionMap NOTES_LIST_PROJECTION_MAP
case NOTE_ID =>
qb setTables "notes"
qb appendWhere ("_id=" + url.getPathSegment(1))
case _ =>
throw new IllegalArgumentException("Unknown URL " + url)
}
// If no sort order is specified use the default
val orderBy =
if (TextUtils isEmpty sort)
NotePad.Notes.DEFAULT_SORT_ORDER
else
sort
val c = qb.query(mDB, projection, selection, selectionArgs, groupBy,
having, orderBy)
c.setNotificationUri(getContext().getContentResolver(), url);
c
}
override def getType(url: ContentURI): String = (URL_MATCHER `match` url) match {
case NOTES =>
"vnd.android.cursor.dir/vnd.google.note"
case NOTE_ID =>
"vnd.android.cursor.item/vnd.google.note"
case _ =>
throw new IllegalArgumentException("Unknown URL " + url)
}
override def insert(url: ContentURI, initialValues: ContentValues): ContentURI = {
val values =
if (initialValues != null)
new ContentValues(initialValues)
else
new ContentValues()
if ((URL_MATCHER `match` url) != NOTES)
throw new IllegalArgumentException("Unknown URL " + url)
val now = System.currentTimeMillis()
val r = Resources.getSystem()
// Make sure that the fields are all set
if (! values.containsKey(NotePad.Notes.CREATED_DATE))
values.put(NotePad.Notes.CREATED_DATE, now)
if (! values.containsKey(NotePad.Notes.MODIFIED_DATE))
values.put(NotePad.Notes.MODIFIED_DATE, now)
if (! values.containsKey(NotePad.Notes.TITLE))
values.put(NotePad.Notes.TITLE, r.getString(_root_.android.R.string.untitled))
if (! values.containsKey(NotePad.Notes.NOTE))
values.put(NotePad.Notes.NOTE, "")
val rowID = mDB.insert("notes", "note", values)
if (rowID > 0) {
val uri = NotePad.Notes.CONTENT_URI addId rowID
getContext().getContentResolver().notifyChange(uri, null)
uri
} else
throw new SQLException("Failed to insert row into " + url)
}
override def delete(url: ContentURI, where: String, whereArgs: Array[String]): Int = {
val count = (URL_MATCHER `match` url) match {
case NOTES =>
mDB.delete("note_pad", where, whereArgs)
case NOTE_ID =>
val segment = url.getPathSegment(1)
//rowId = Long.parseLong(segment);
mDB.delete("notes", "_id=" +
segment +
(if (!TextUtils.isEmpty(where)) " AND (" + where + ')' else ""),
whereArgs)
case _ =>
throw new IllegalArgumentException("Unknown URL " + url)
}
getContext().getContentResolver().notifyChange(url, null)
count
}
override def update(url: ContentURI, values: ContentValues,
where: String, whereArgs: Array[String]): Int = {
val count = (URL_MATCHER `match` url) match {
case NOTES =>
mDB.update("notes", values, where, whereArgs)
case NOTE_ID =>
val segment = url getPathSegment 1
mDB.update("notes", values, "_id=" +
segment +
(if (!TextUtils.isEmpty(where)) " AND (" + where + ')'
else ""),
whereArgs)
case _ =>
throw new IllegalArgumentException("Unknown URL " + url)
}
getContext().getContentResolver().notifyChange(url, null)
count
}
}

View File

@ -0,0 +1,203 @@
/*
* Copyright (C) 2007 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.notepad
import com.google.provider.NotePad
import _root_.android.app.Activity._
import _root_.android.app.ListActivity
import _root_.android.content.{ComponentName, Intent}
import _root_.android.database.Cursor
import _root_.android.graphics.drawable.Drawable
import _root_.android.net.ContentURI
import _root_.android.os.Bundle
import _root_.android.view.{KeyEvent, Menu, View}
import _root_.android.view.View.MeasureSpec
import _root_.android.widget.{ListAdapter, ListView, SimpleCursorAdapter, TextView}
/**
* Displays a list of notes. Will display notes from the ContentUri
* provided int the intent if there is one, otherwise uses the default list
* from the
*
*/
object NotesList {
// Menu item Ids
val DELETE_ID = Menu.FIRST
val INSERT_ID = Menu.FIRST + 1
/**
* The columns we are interested in from the database
*/
private val PROJECTION = Array(
NotePad.Notes._ID,
NotePad.Notes.TITLE
)
}
class NotesList extends ListActivity {
import NotesList._ // companion object
/**
* Cursor which holds list of all notes
*/
private var mCursor: Cursor = _
override protected def onCreate(icicle: Bundle) {
super.onCreate(icicle)
setDefaultKeyMode(SHORTCUT_DEFAULT_KEYS)
// If no data was given in the intent (because we were started
// as a MAIN activity), then use our default content provider.
val intent = getIntent()
if (intent.getData() == null)
intent setData NotePad.Notes.CONTENT_URI
setupListStripes()
mCursor = managedQuery(getIntent().getData(), PROJECTION, null, null)
// Used to map notes entries from the database to views
val adapter = new SimpleCursorAdapter(this,
_root_.android.R.layout.simple_list_item_1, mCursor,
Array(NotePad.Notes.TITLE), Array(_root_.android.R.id.text1))
setListAdapter(adapter)
}
/**
* Add stripes to the list view.
*/
private def setupListStripes() {
// Get Drawables for alternating stripes
val lineBackgrounds = new Array[Drawable](2)
lineBackgrounds(0) = getResources().getDrawable(R.drawable.even_stripe)
lineBackgrounds(1) = getResources().getDrawable(R.drawable.odd_stripe)
// Make and measure a sample TextView of the sort our adapter will
// return
val view = getViewInflate().
inflate(_root_.android.R.layout.simple_list_item_1, null, null)
val v = view.findViewById(_root_.android.R.id.text1).asInstanceOf[TextView]
v setText "X"
// Make it 100 pixels wide, and let it choose its own height.
v.measure(MeasureSpec.makeMeasureSpec(View.MeasureSpec.EXACTLY, 100),
MeasureSpec.makeMeasureSpec(View.MeasureSpec.UNSPECIFIED, 0))
val height = v.getMeasuredHeight()
getListView().setStripes(lineBackgrounds, height)
}
override def onCreateOptionsMenu(menu: Menu): Boolean = {
super.onCreateOptionsMenu(menu)
// This is our one standard application action -- inserting a
// new note into the list.
menu.add(0, INSERT_ID, R.string.menu_insert).setShortcut(
KeyEvent.KEYCODE_3, 0, KeyEvent.KEYCODE_A)
// Generate any additional actions that can be performed on the
// overall list. In a normal install, there are no additional
// actions found here, but this allows other applications to extend
// our menu with their own actions.
val intent = new Intent(null, getIntent().getData())
intent addCategory Intent.ALTERNATIVE_CATEGORY
menu.addIntentOptions(
Menu.ALTERNATIVE, 0, new ComponentName(this, classOf[NotesList]),
null, intent, 0, null)
true
}
override def onPrepareOptionsMenu(menu: Menu): Boolean = {
super.onPrepareOptionsMenu(menu)
val haveItems = mCursor.count() > 0
// If there are any notes in the list (which implies that one of
// them is selected), then we need to generate the actions that
// can be performed on the current selection. This will be a combination
// of our own specific actions along with any extensions that can be
// found.
if (haveItems) {
// This is the selected item.
val uri = getIntent().getData() addId getSelectionRowID()
// Build menu... always starts with the EDIT action...
val specifics = Array(new Intent(Intent.EDIT_ACTION, uri))
val items = new Array[Menu.Item](1)
// ... is followed by whatever other actions are available...
val intent = new Intent(null, uri)
intent addCategory Intent.SELECTED_ALTERNATIVE_CATEGORY
menu.addIntentOptions(Menu.SELECTED_ALTERNATIVE, 0, null, specifics,
intent, Menu.NO_SEPARATOR_AFTER, items)
// ... and ends with the delete command.
menu.add(Menu.SELECTED_ALTERNATIVE, DELETE_ID, R.string.menu_delete).
setShortcut(KeyEvent.KEYCODE_2, 0, KeyEvent.KEYCODE_D)
menu.addSeparator(Menu.SELECTED_ALTERNATIVE, 0)
// Give a shortcut to the edit action.
if (items(0) != null)
items(0).setShortcut(KeyEvent.KEYCODE_1, 0, KeyEvent.KEYCODE_E)
}
else
menu removeGroup Menu.SELECTED_ALTERNATIVE
// Make sure the delete action is disabled if there are no items.
menu.setItemShown(DELETE_ID, haveItems)
true
}
override def onOptionsItemSelected(item: Menu.Item): Boolean = item.getId() match {
case DELETE_ID =>
deleteItem()
true
case INSERT_ID =>
insertItem()
true
case _ =>
super.onOptionsItemSelected(item)
}
override protected def onListItemClick(l: ListView, v: View,
position: Int, id: Long) {
val url = getIntent().getData() addId getSelectionRowID()
val action = getIntent().getAction()
if ((Intent.PICK_ACTION equals action) ||
(Intent.GET_CONTENT_ACTION equals action))
// The caller is waiting for us to return a note selected by
// the user. The have clicked on one, so return it now.
setResult(RESULT_OK, url.toString())
else
// Launch activity to view/edit the currently selected item
startActivity(new Intent(Intent.EDIT_ACTION, url))
}
private final def deleteItem() {
mCursor moveTo getSelection()
mCursor.deleteRow()
}
private final def insertItem() {
// Launch activity to insert a new item
startActivity(new Intent(Intent.INSERT_ACTION, getIntent().getData()))
}
}

View File

@ -0,0 +1,115 @@
/*
* Copyright (C) 2007 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.notepad
import com.google.provider.NotePad
import _root_.android.app.Activity
import _root_.android.database.Cursor
import _root_.android.net.ContentURI
import _root_.android.os.Bundle
import _root_.android.view.View
import _root_.android.widget.{Button, EditText}
/**
* An activity that will edit the title of a note. Displays a floating
* window with a text field.
*/
object TitleEditor {
/**
* This is a special intent action that means "edit the title of a note".
*/
val EDIT_TITLE_ACTION =
"com.google.android.notepad.action.EDIT_TITLE"
/**
* Index of the title column
*/
private val TITLE_INDEX = 1
/**
* An array of the columns we are interested in.
*/
private val PROJECTION = Array(
NotePad.Notes._ID, // 0
NotePad.Notes.TITLE, // 1
)
}
class TitleEditor extends Activity with View.OnClickListener {
import TitleEditor._ // companion object
/**
* Cursor which will provide access to the note whose title we are editing.
*/
var mCursor: Cursor = _
/**
* The EditText field from our UI. Keep track of this so we can extract the
* text when we are finished.
*/
var mText: EditText = _
override def onCreate(icicle: Bundle) {
super.onCreate(icicle)
setContentView(R.layout.title_editor)
// Get the uri of the note whose title we want to edit
val uri = getIntent().getData()
// Get a cursor to access the note
mCursor = managedQuery(uri, PROJECTION, null, null)
// Set up click handlers for the text field and button
mText = this.findViewById(R.id.title).asInstanceOf[EditText]
mText setOnClickListener this
val b = /*(Button)*/ findViewById(R.id.ok)
b setOnClickListener this
}
override protected def onResume() {
super.onResume()
// Initialize the text with the title column from the cursor
if (mCursor != null) {
mCursor.first()
val title = mCursor getString TITLE_INDEX
mText setText title
}
}
override protected def onPause() {
super.onPause()
// Write the text back into the cursor
if (mCursor != null) {
val title = mText.getText().toString()
mCursor.updateString(TITLE_INDEX, title)
mCursor.commitUpdates()
}
}
def onClick(v: View) {
// When the user clicks, just finish this activity.
// onPause will be called, and we save our data there.
finish()
}
}

View File

@ -0,0 +1,69 @@
/*
* Copyright (C) 2007 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.provider
import _root_.android.net.ContentURI
import _root_.android.provider.BaseColumns
/**
* Convenience definitions for NotePadProvider
*/
object NotePad {
/**
* Notes table
*/
object Notes {
val _ID = BaseColumns._ID
val _COUNT = BaseColumns._COUNT
/**
* The content:// style URL for this table
*/
val CONTENT_URI =
ContentURI.create("content://com.google.provider.NotePad/notes")
/**
* The default sort order for this table
*/
val DEFAULT_SORT_ORDER = "modified DESC"
/**
* The title of the note
* <P>Type: TEXT</P>
*/
val TITLE = "title"
/**
* The note itself
* <P>Type: TEXT</P>
*/
val NOTE = "note"
/**
* The timestamp for when the note was created
* <P>Type: INTEGER (long)</P>
*/
val CREATED_DATE = "created"
/**
* The timestamp for when the note was last modified
* <P>Type: INTEGER (long)</P>
*/
val MODIFIED_DATE = "modified"
}
}