Original code by Jeffrey Sharkey <jsharkey@android.com>. Modifications: - Remove .classpath, .project, and default.properties. - Remove generated files (/gen). - Create Android.mk and _index.html. - Cleaned up whitespace and converted to match AOSP style guide. - Renamed SimpleWiktionary to WiktionarySimple to keep both samples next to each other in the directory listing. - Removed the android:text attribute in the BulletPoint due to localization issues.
345 lines
12 KiB
Java
345 lines
12 KiB
Java
/*
|
|
* Copyright (C) 2009 The Android Open Source Project
|
|
*
|
|
* 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.example.android.wiktionary;
|
|
|
|
import com.example.android.wiktionary.SimpleWikiHelper.ApiException;
|
|
import com.example.android.wiktionary.SimpleWikiHelper.ParseException;
|
|
|
|
import android.app.Activity;
|
|
import android.app.AlertDialog;
|
|
import android.app.SearchManager;
|
|
import android.content.Intent;
|
|
import android.net.Uri;
|
|
import android.os.AsyncTask;
|
|
import android.os.Bundle;
|
|
import android.os.SystemClock;
|
|
import android.text.TextUtils;
|
|
import android.text.format.DateUtils;
|
|
import android.util.Log;
|
|
import android.view.KeyEvent;
|
|
import android.view.Menu;
|
|
import android.view.MenuInflater;
|
|
import android.view.MenuItem;
|
|
import android.view.View;
|
|
import android.view.animation.Animation;
|
|
import android.view.animation.AnimationUtils;
|
|
import android.view.animation.Animation.AnimationListener;
|
|
import android.webkit.WebView;
|
|
import android.widget.ProgressBar;
|
|
import android.widget.TextView;
|
|
|
|
import java.util.Stack;
|
|
|
|
/**
|
|
* Activity that lets users browse through Wiktionary content. This is just the
|
|
* user interface, and all API communication and parsing is handled in
|
|
* {@link ExtendedWikiHelper}.
|
|
*/
|
|
public class LookupActivity extends Activity implements AnimationListener {
|
|
private static final String TAG = "LookupActivity";
|
|
|
|
private View mTitleBar;
|
|
private TextView mTitle;
|
|
private ProgressBar mProgress;
|
|
private WebView mWebView;
|
|
|
|
private Animation mSlideIn;
|
|
private Animation mSlideOut;
|
|
|
|
/**
|
|
* History stack of previous words browsed in this session. This is
|
|
* referenced when the user taps the "back" key, to possibly intercept and
|
|
* show the last-visited entry, instead of closing the activity.
|
|
*/
|
|
private Stack<String> mHistory = new Stack<String>();
|
|
|
|
private String mEntryTitle;
|
|
|
|
/**
|
|
* Keep track of last time user tapped "back" hard key. When pressed more
|
|
* than once within {@link #BACK_THRESHOLD}, we treat let the back key fall
|
|
* through and close the app.
|
|
*/
|
|
private long mLastPress = -1;
|
|
|
|
private static final long BACK_THRESHOLD = DateUtils.SECOND_IN_MILLIS / 2;
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
@Override
|
|
public void onCreate(Bundle savedInstanceState) {
|
|
super.onCreate(savedInstanceState);
|
|
|
|
setContentView(R.layout.lookup);
|
|
|
|
// Load animations used to show/hide progress bar
|
|
mSlideIn = AnimationUtils.loadAnimation(this, R.anim.slide_in);
|
|
mSlideOut = AnimationUtils.loadAnimation(this, R.anim.slide_out);
|
|
|
|
// Listen for the "in" animation so we make the progress bar visible
|
|
// only after the sliding has finished.
|
|
mSlideIn.setAnimationListener(this);
|
|
|
|
mTitleBar = findViewById(R.id.title_bar);
|
|
mTitle = (TextView) findViewById(R.id.title);
|
|
mProgress = (ProgressBar) findViewById(R.id.progress);
|
|
mWebView = (WebView) findViewById(R.id.webview);
|
|
|
|
// Make the view transparent to show background
|
|
mWebView.setBackgroundColor(0);
|
|
|
|
// Prepare User-Agent string for wiki actions
|
|
ExtendedWikiHelper.prepareUserAgent(this);
|
|
|
|
// Handle incoming intents as possible searches or links
|
|
onNewIntent(getIntent());
|
|
}
|
|
|
|
/**
|
|
* Intercept the back-key to try walking backwards along our word history
|
|
* stack. If we don't have any remaining history, the key behaves normally
|
|
* and closes this activity.
|
|
*/
|
|
@Override
|
|
public boolean onKeyDown(int keyCode, KeyEvent event) {
|
|
// Handle back key as long we have a history stack
|
|
if (keyCode == KeyEvent.KEYCODE_BACK && !mHistory.empty()) {
|
|
|
|
// Compare against last pressed time, and if user hit multiple times
|
|
// in quick succession, we should consider bailing out early.
|
|
long currentPress = SystemClock.uptimeMillis();
|
|
if (currentPress - mLastPress < BACK_THRESHOLD) {
|
|
return super.onKeyDown(keyCode, event);
|
|
}
|
|
mLastPress = currentPress;
|
|
|
|
// Pop last entry off stack and start loading
|
|
String lastEntry = mHistory.pop();
|
|
startNavigating(lastEntry, false);
|
|
|
|
return true;
|
|
}
|
|
|
|
// Otherwise fall through to parent
|
|
return super.onKeyDown(keyCode, event);
|
|
}
|
|
|
|
/**
|
|
* Start navigating to the given word, pushing any current word onto the
|
|
* history stack if requested. The navigation happens on a background thread
|
|
* and updates the GUI when finished.
|
|
*
|
|
* @param word The dictionary word to navigate to.
|
|
* @param pushHistory If true, push the current word onto history stack.
|
|
*/
|
|
private void startNavigating(String word, boolean pushHistory) {
|
|
// Push any current word onto the history stack
|
|
if (!TextUtils.isEmpty(mEntryTitle) && pushHistory) {
|
|
mHistory.add(mEntryTitle);
|
|
}
|
|
|
|
// Start lookup for new word in background
|
|
new LookupTask().execute(word);
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
@Override
|
|
public boolean onCreateOptionsMenu(Menu menu) {
|
|
MenuInflater inflater = getMenuInflater();
|
|
inflater.inflate(R.menu.lookup, menu);
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
@Override
|
|
public boolean onOptionsItemSelected(MenuItem item) {
|
|
switch (item.getItemId()) {
|
|
case R.id.lookup_search: {
|
|
onSearchRequested();
|
|
return true;
|
|
}
|
|
case R.id.lookup_random: {
|
|
startNavigating(null, true);
|
|
return true;
|
|
}
|
|
case R.id.lookup_about: {
|
|
showAbout();
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Show an about dialog that cites data sources.
|
|
*/
|
|
protected void showAbout() {
|
|
// Inflate the about message contents
|
|
View messageView = getLayoutInflater().inflate(R.layout.about, null, false);
|
|
|
|
// When linking text, force to always use default color. This works
|
|
// around a pressed color state bug.
|
|
TextView textView = (TextView) messageView.findViewById(R.id.about_credits);
|
|
int defaultColor = textView.getTextColors().getDefaultColor();
|
|
textView.setTextColor(defaultColor);
|
|
|
|
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
|
builder.setIcon(R.drawable.app_icon);
|
|
builder.setTitle(R.string.app_name);
|
|
builder.setView(messageView);
|
|
builder.create();
|
|
builder.show();
|
|
}
|
|
|
|
/**
|
|
* Because we're singleTop, we handle our own new intents. These usually
|
|
* come from the {@link SearchManager} when a search is requested, or from
|
|
* internal links the user clicks on.
|
|
*/
|
|
@Override
|
|
public void onNewIntent(Intent intent) {
|
|
final String action = intent.getAction();
|
|
if (Intent.ACTION_SEARCH.equals(action)) {
|
|
// Start query for incoming search request
|
|
String query = intent.getStringExtra(SearchManager.QUERY);
|
|
startNavigating(query, true);
|
|
|
|
} else if (Intent.ACTION_VIEW.equals(action)) {
|
|
// Treat as internal link only if valid Uri and host matches
|
|
Uri data = intent.getData();
|
|
if (data != null && ExtendedWikiHelper.WIKI_LOOKUP_HOST
|
|
.equals(data.getHost())) {
|
|
String query = data.getPathSegments().get(0);
|
|
startNavigating(query, true);
|
|
}
|
|
|
|
} else {
|
|
// If not recognized, then start showing random word
|
|
startNavigating(null, true);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Set the title for the current entry.
|
|
*/
|
|
protected void setEntryTitle(String entryText) {
|
|
mEntryTitle = entryText;
|
|
mTitle.setText(mEntryTitle);
|
|
}
|
|
|
|
/**
|
|
* Set the content for the current entry. This will update our
|
|
* {@link WebView} to show the requested content.
|
|
*/
|
|
protected void setEntryContent(String entryContent) {
|
|
mWebView.loadDataWithBaseURL(ExtendedWikiHelper.WIKI_AUTHORITY, entryContent,
|
|
ExtendedWikiHelper.MIME_TYPE, ExtendedWikiHelper.ENCODING, null);
|
|
}
|
|
|
|
/**
|
|
* Background task to handle Wiktionary lookups. This correctly shows and
|
|
* hides the loading animation from the GUI thread before starting a
|
|
* background query to the Wiktionary API. When finished, it transitions
|
|
* back to the GUI thread where it updates with the newly-found entry.
|
|
*/
|
|
private class LookupTask extends AsyncTask<String, String, String> {
|
|
/**
|
|
* Before jumping into background thread, start sliding in the
|
|
* {@link ProgressBar}. We'll only show it once the animation finishes.
|
|
*/
|
|
@Override
|
|
protected void onPreExecute() {
|
|
mTitleBar.startAnimation(mSlideIn);
|
|
}
|
|
|
|
/**
|
|
* Perform the background query using {@link ExtendedWikiHelper}, which
|
|
* may return an error message as the result.
|
|
*/
|
|
@Override
|
|
protected String doInBackground(String... args) {
|
|
String query = args[0];
|
|
String parsedText = null;
|
|
|
|
try {
|
|
// If query word is null, assume request for random word
|
|
if (query == null) {
|
|
query = ExtendedWikiHelper.getRandomWord();
|
|
}
|
|
|
|
if (query != null) {
|
|
// Push our requested word to the title bar
|
|
publishProgress(query);
|
|
String wikiText = ExtendedWikiHelper.getPageContent(query, true);
|
|
parsedText = ExtendedWikiHelper.formatWikiText(wikiText);
|
|
}
|
|
} catch (ApiException e) {
|
|
Log.e(TAG, "Problem making wiktionary request", e);
|
|
} catch (ParseException e) {
|
|
Log.e(TAG, "Problem making wiktionary request", e);
|
|
}
|
|
|
|
if (parsedText == null) {
|
|
parsedText = getString(R.string.empty_result);
|
|
}
|
|
|
|
return parsedText;
|
|
}
|
|
|
|
/**
|
|
* Our progress update pushes a title bar update.
|
|
*/
|
|
@Override
|
|
protected void onProgressUpdate(String... args) {
|
|
String searchWord = args[0];
|
|
setEntryTitle(searchWord);
|
|
}
|
|
|
|
/**
|
|
* When finished, push the newly-found entry content into our
|
|
* {@link WebView} and hide the {@link ProgressBar}.
|
|
*/
|
|
@Override
|
|
protected void onPostExecute(String parsedText) {
|
|
mTitleBar.startAnimation(mSlideOut);
|
|
mProgress.setVisibility(View.INVISIBLE);
|
|
|
|
setEntryContent(parsedText);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Make the {@link ProgressBar} visible when our in-animation finishes.
|
|
*/
|
|
public void onAnimationEnd(Animation animation) {
|
|
mProgress.setVisibility(View.VISIBLE);
|
|
}
|
|
|
|
public void onAnimationRepeat(Animation animation) {
|
|
// Not interested if the animation repeats
|
|
}
|
|
|
|
public void onAnimationStart(Animation animation) {
|
|
// Not interested when the animation starts
|
|
}
|
|
}
|