summaryrefslogtreecommitdiffstats
path: root/src/com/android/swe/browser/homepages/RequestHandler.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android/swe/browser/homepages/RequestHandler.java')
-rw-r--r--src/com/android/swe/browser/homepages/RequestHandler.java264
1 files changed, 264 insertions, 0 deletions
diff --git a/src/com/android/swe/browser/homepages/RequestHandler.java b/src/com/android/swe/browser/homepages/RequestHandler.java
new file mode 100644
index 00000000..c75ffcba
--- /dev/null
+++ b/src/com/android/swe/browser/homepages/RequestHandler.java
@@ -0,0 +1,264 @@
+
+/*
+ * Copyright (C) 2011 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.android.browser.homepages;
+
+import android.content.Context;
+import android.content.UriMatcher;
+import android.content.res.Resources;
+import android.database.Cursor;
+import android.database.MergeCursor;
+import android.net.Uri;
+import android.provider.BrowserContract.Bookmarks;
+import android.provider.BrowserContract.History;
+import android.text.TextUtils;
+import android.util.Base64;
+import android.util.Log;
+
+import com.android.browser.R;
+import com.android.browser.homepages.Template.ListEntityIterator;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.text.DateFormat;
+import java.text.DecimalFormat;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class RequestHandler extends Thread {
+
+ private static final String TAG = "RequestHandler";
+ private static final int INDEX = 1;
+ private static final int RESOURCE = 2;
+ private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
+
+ Uri mUri;
+ Context mContext;
+ OutputStream mOutput;
+
+ static {
+ sUriMatcher.addURI(HomeProvider.AUTHORITY, "index", INDEX);
+ sUriMatcher.addURI(HomeProvider.AUTHORITY, "res/*/*", RESOURCE);
+ }
+
+ public RequestHandler(Context context, Uri uri, OutputStream out) {
+ mUri = uri;
+ mContext = context.getApplicationContext();
+ mOutput = out;
+ }
+
+ @Override
+ public void run() {
+ super.run();
+ try {
+ doHandleRequest();
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to handle request: " + mUri, e);
+ } finally {
+ cleanup();
+ }
+ }
+
+ void doHandleRequest() throws IOException {
+ if ("file".equals(mUri.getScheme())) {
+ writeFolderIndex();
+ return;
+ }
+ int match = sUriMatcher.match(mUri);
+ switch (match) {
+ case INDEX:
+ writeTemplatedIndex();
+ break;
+ case RESOURCE:
+ writeResource(getUriResourcePath());
+ break;
+ }
+ }
+
+ byte[] htmlEncode(String s) {
+ return TextUtils.htmlEncode(s).getBytes();
+ }
+
+ // We can reuse this for both History and Bookmarks queries because the
+ // columns defined actually belong to the CommonColumn and ImageColumn
+ // interfaces that both History and Bookmarks implement
+ private static final String[] PROJECTION = new String[] {
+ History.URL,
+ History.TITLE,
+ History.THUMBNAIL
+ };
+ private static final String SELECTION = History.URL
+ + " NOT LIKE 'content:%' AND " + History.THUMBNAIL + " IS NOT NULL";
+ void writeTemplatedIndex() throws IOException {
+ Template t = Template.getCachedTemplate(mContext, R.raw.most_visited);
+ Cursor historyResults = mContext.getContentResolver().query(
+ History.CONTENT_URI, PROJECTION, SELECTION,
+ null, History.VISITS + " DESC LIMIT 12");
+ Cursor cursor = historyResults;
+ try {
+ if (cursor.getCount() < 12) {
+ Cursor bookmarkResults = mContext.getContentResolver().query(
+ Bookmarks.CONTENT_URI, PROJECTION, SELECTION,
+ null, Bookmarks.DATE_CREATED + " DESC LIMIT 12");
+ cursor = new MergeCursor(new Cursor[] { historyResults, bookmarkResults }) {
+ @Override
+ public int getCount() {
+ return Math.min(12, super.getCount());
+ }
+ };
+ }
+ t.assignLoop("most_visited", new Template.CursorListEntityWrapper(cursor) {
+ @Override
+ public void writeValue(OutputStream stream, String key) throws IOException {
+ Cursor cursor = getCursor();
+ if (key.equals("url")) {
+ stream.write(htmlEncode(cursor.getString(0)));
+ } else if (key.equals("title")) {
+ stream.write(htmlEncode(cursor.getString(1)));
+ } else if (key.equals("thumbnail")) {
+ stream.write("data:image/png;base64,".getBytes());
+ byte[] thumb = cursor.getBlob(2);
+ stream.write(Base64.encode(thumb, Base64.DEFAULT));
+ }
+ }
+ });
+ t.write(mOutput);
+ } finally {
+ cursor.close();
+ }
+ }
+
+ private static final Comparator<File> sFileComparator = new Comparator<File>() {
+ @Override
+ public int compare(File lhs, File rhs) {
+ if (lhs.isDirectory() != rhs.isDirectory()) {
+ return lhs.isDirectory() ? -1 : 1;
+ }
+ return lhs.getName().compareTo(rhs.getName());
+ }
+ };
+
+ void writeFolderIndex() throws IOException {
+ File f = new File(mUri.getPath());
+ final File[] files = f.listFiles();
+ Arrays.sort(files, sFileComparator);
+ Template t = Template.getCachedTemplate(mContext, R.raw.folder_view);
+ t.assign("path", mUri.getPath());
+ t.assign("parent_url", f.getParent() != null ? f.getParent() : f.getPath());
+ t.assignLoop("files", new ListEntityIterator() {
+ int index = -1;
+
+ @Override
+ public void writeValue(OutputStream stream, String key) throws IOException {
+ File f = files[index];
+ if ("name".equals(key)) {
+ stream.write(f.getName().getBytes());
+ }
+ if ("url".equals(key)) {
+ stream.write(("file://" + f.getAbsolutePath()).getBytes());
+ }
+ if ("type".equals(key)) {
+ stream.write((f.isDirectory() ? "dir" : "file").getBytes());
+ }
+ if ("size".equals(key)) {
+ if (f.isFile()) {
+ stream.write(readableFileSize(f.length()).getBytes());
+ }
+ }
+ if ("last_modified".equals(key)) {
+ String date = DateFormat.getDateTimeInstance(
+ DateFormat.SHORT, DateFormat.SHORT)
+ .format(f.lastModified());
+ stream.write(date.getBytes());
+ }
+ if ("alt".equals(key)) {
+ if (index % 2 == 0) {
+ stream.write("alt".getBytes());
+ }
+ }
+ }
+
+ @Override
+ public ListEntityIterator getListIterator(String key) {
+ return null;
+ }
+
+ @Override
+ public void reset() {
+ index = -1;
+ }
+
+ @Override
+ public boolean moveToNext() {
+ return (++index) < files.length;
+ }
+ });
+ t.write(mOutput);
+ }
+
+ static String readableFileSize(long size) {
+ if(size <= 0) return "0";
+ final String[] units = new String[] { "B", "KB", "MB", "GB", "TB" };
+ int digitGroups = (int) (Math.log10(size) / Math.log10(1024));
+ return new DecimalFormat("#,##0.#").format(
+ size / Math.pow(1024, digitGroups)) + " " + units[digitGroups];
+ }
+
+ String getUriResourcePath() {
+ final Pattern pattern = Pattern.compile("/?res/([\\w/]+)");
+ Matcher m = pattern.matcher(mUri.getPath());
+ if (m.matches()) {
+ return m.group(1);
+ } else {
+ return mUri.getPath();
+ }
+ }
+
+ void writeResource(String fileName) throws IOException {
+ Resources res = mContext.getResources();
+ String packageName = R.class.getPackage().getName();
+ int id = res.getIdentifier(fileName, null, packageName);
+ if (id != 0) {
+ InputStream in = res.openRawResource(id);
+ byte[] buf = new byte[4096];
+ int read;
+ while ((read = in.read(buf)) > 0) {
+ mOutput.write(buf, 0, read);
+ }
+ }
+ }
+
+ void writeString(String str) throws IOException {
+ mOutput.write(str.getBytes());
+ }
+
+ void writeString(String str, int offset, int count) throws IOException {
+ mOutput.write(str.getBytes(), offset, count);
+ }
+
+ void cleanup() {
+ try {
+ mOutput.close();
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to close pipe!", e);
+ }
+ }
+
+}