Bug 667530 - Add ability to add application/bookmark shortcuts to Launcher screens [r=blassey, r=fabrice]
☠☠ backed out by bc91728e5a7c ☠ ☠
authorWesley Johnston <wjohnston@mozilla.com>
Fri, 01 Jul 2011 14:26:00 -0400
changeset 73307 89ef5bf1e3d2f3af936e9a3ba49f95a2f4e26afd
parent 73306 9e99bd095fe1f55833aaa7ec6f2b48d406239871
child 73308 470cfda4dd83e6df5ea40ce283accb51e317afc8
push idunknown
push userunknown
push dateunknown
reviewersblassey, fabrice
bugs667530
milestone8.0a1
Bug 667530 - Add ability to add application/bookmark shortcuts to Launcher screens [r=blassey, r=fabrice]
embedding/android/AndroidManifest.xml.in
embedding/android/GeckoApp.java
embedding/android/GeckoAppShell.java
embedding/android/LauncherShortcuts.java.in
embedding/android/Makefile.in
embedding/android/locales/en-US/android_strings.dtd
embedding/android/resources/layout/launch_app_list.xml
embedding/android/resources/layout/launch_app_listitem.xml
embedding/android/strings.xml.in
--- a/embedding/android/AndroidManifest.xml.in
+++ b/embedding/android/AndroidManifest.xml.in
@@ -22,16 +22,25 @@
     <application android:label="@MOZ_APP_DISPLAYNAME@"
 		 android:icon="@drawable/icon"
 #if MOZILLA_OFFICIAL
 		 android:debuggable="false">
 #else
 		 android:debuggable="true">
 #endif
 
+        <activity android:name="LauncherShortcuts"
+                  android:label="@string/launcher_shortcuts_title">
+            <!--  This intent-filter allows your shortcuts to be created in the launcher. -->
+            <intent-filter>
+                <action android:name="android.intent.action.CREATE_SHORTCUT" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity>
+
         <activity android:name="App"
                   android:label="@MOZ_APP_DISPLAYNAME@"
                   android:configChanges="keyboard|keyboardHidden|mcc|mnc"
                   android:windowSoftInputMode="stateUnspecified|adjustResize"
                   android:launchMode="singleTask"
                   android:theme="@style/GreyTheme">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
--- a/embedding/android/GeckoApp.java
+++ b/embedding/android/GeckoApp.java
@@ -298,17 +298,17 @@ abstract public class GeckoApp
             String uri = intent.getDataString();
             GeckoAppShell.sendEventToGecko(new GeckoEvent(uri));
             Log.i("GeckoApp","onNewIntent: "+uri);
         }
         else if (Intent.ACTION_MAIN.equals(action)) {
             Log.i("GeckoApp", "Intent : ACTION_MAIN");
             GeckoAppShell.sendEventToGecko(new GeckoEvent(""));
         }
-        else if (action.equals("org.mozilla.fennec.WEBAPP")) {
+        else if (action.equals("org.mozilla.gecko.WEBAPP")) {
             String uri = intent.getStringExtra("args");
             GeckoAppShell.sendEventToGecko(new GeckoEvent(uri));
             Log.i("GeckoApp","Intent : WEBAPP - " + uri);
         }
     }
 
     @Override
     public void onPause()
--- a/embedding/android/GeckoAppShell.java
+++ b/embedding/android/GeckoAppShell.java
@@ -673,17 +673,17 @@ public class GeckoAppShell
         gRestartScheduled = true;
     }
 
     // "Installs" an application by creating a shortcut
     static void installWebApplication(String aURI, String aTitle, String aIconData) {
         Log.w("GeckoAppJava", "installWebApplication for " + aURI + " [" + aTitle + "]");
 
         // the intent to be launched by the shortcut
-        Intent shortcutIntent = new Intent("org.mozilla.fennec.WEBAPP");
+        Intent shortcutIntent = new Intent("org.mozilla.gecko.WEBAPP");
         shortcutIntent.setClassName(GeckoApp.mAppContext,
                                     GeckoApp.mAppContext.getPackageName() + ".App");
         shortcutIntent.putExtra("args", "--webapp=" + aURI);
 
         Intent intent = new Intent();
         intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
         intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, aTitle);
         byte[] raw = Base64.decode(aIconData.substring(22), Base64.DEFAULT);
new file mode 100644
--- /dev/null
+++ b/embedding/android/LauncherShortcuts.java.in
@@ -0,0 +1,184 @@
+/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (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.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Android code.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Vladimir Vukicevic <vladimir@pobox.com>
+ *   Wes Johnston <wjohnston@mozilla.com>
+ *   Mark Finkle <mfinkle@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#filter substitution
+package @ANDROID_PACKAGE_NAME@;
+
+import java.io.*;
+
+import org.mozilla.gecko.*;
+
+import android.os.*;
+import android.content.*;
+import android.app.*;
+import android.text.*;
+import android.util.*;
+import android.widget.*;
+import android.database.sqlite.*;
+import android.database.*;
+import android.view.*;
+import android.net.Uri;
+import android.graphics.*;
+
+
+public class LauncherShortcuts extends ListActivity {
+    public static final String CREATE_SHORTCUT = "org.mozilla.gecko.CREATE_SHORTCUT";
+
+    public class LauncherCursorAdapter extends SimpleCursorAdapter {
+        private Cursor _cursor;
+        private Context _context;
+
+        public LauncherCursorAdapter(Context context, int layout, Cursor c, String[] from, int[] to) {
+            // Using the older, deprecated constructor so we can work on API < 11
+            super(context, layout, c, from, to);
+            _cursor = c;
+            _context = context;
+        }
+
+        @Override
+        public void bindView(View view, Context context, Cursor cursor) {
+            ImageView imageView = (ImageView) view.findViewById(R.id.favicon);
+    
+            String favicon = cursor.getString(3);
+            byte[] raw = Base64.decode(favicon.substring(22), Base64.DEFAULT);
+            Bitmap bitmap = Bitmap.createScaledBitmap(BitmapFactory.decodeByteArray(raw, 0, raw.length), 48, 48, true);
+            imageView.setImageBitmap(bitmap);
+
+            super.bindView(view, context, cursor);
+        }
+    }
+
+    private Cursor getCursor(Context context) {
+        File home = new File(context.getFilesDir(), "mozilla");
+        if (!home.exists())
+            return null;
+
+        File profile = null;
+        String[] files = home.list();
+        for (int i = 0; i < files.length; i++) {
+            if (files[i].endsWith(".default")) {
+                profile = new File(home, files[i]);
+                break;
+            }
+        }
+
+        if (profile == null)
+            return null;
+
+        File webapps = new File(profile, "webapps.sqlite");
+        if (!webapps.exists())
+            return null;
+
+        Log.i("LauncherShortcuts", "Opening: " + webapps.getPath());
+        mDb = SQLiteDatabase.openDatabase(webapps.getPath(), null, SQLiteDatabase.OPEN_READONLY | SQLiteDatabase.NO_LOCALIZED_COLLATORS);
+        return mDb.rawQuery("SELECT rowid as _id, title, uri, icon FROM webapps", null);
+    }
+    
+    private Cursor mCursor;
+    private SQLiteDatabase mDb;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        setContentView(R.layout.launch_app_list);
+
+        final Intent intent = getIntent();
+        final String action = intent.getAction();
+
+        if (Intent.ACTION_CREATE_SHORTCUT.equals(action)) {
+            mCursor = getCursor(this);
+            if (mCursor != null) {
+                // After selecting an item, the empty view can flash on screen. Clear
+                // the text so we don't see it.
+                TextView emptyText = (TextView)findViewById(android.R.id.empty);
+                emptyText.setText("");
+
+                // Load the list using a custom adapter so we can create the bitmaps
+                ListAdapter adapter = new LauncherCursorAdapter(
+                    this,
+                    R.layout.launch_app_listitem,
+                    mCursor,
+                    new String[] {"title"},
+                    new int[] {R.id.title}
+                );
+                setListAdapter(adapter);
+            }
+        }
+    }
+
+    @Override
+    public void onListItemClick(ListView l, View v, int position, long id) {
+        mCursor.moveToPosition(position);
+
+        Intent shortcutintent = new Intent("org.mozilla.gecko.WEBAPP");
+        shortcutintent.setClass(this, App.class);
+        shortcutintent.putExtra("args", "--webapp=" + mCursor.getString(2));
+
+        Intent intent = new Intent();
+        intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, mCursor.getString(1));
+        intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutintent);
+
+        String favicon = mCursor.getString(3);
+        byte[] raw = Base64.decode(favicon.substring(22), Base64.DEFAULT);
+
+        DisplayMetrics dm = new DisplayMetrics();
+        getWindowManager().getDefaultDisplay().getMetrics(dm);
+        int size;
+        switch (dm.densityDpi) {
+            case DisplayMetrics.DENSITY_MEDIUM:
+                size = 48;
+                break;
+            case DisplayMetrics.DENSITY_HIGH:
+                size = 72;
+                break;
+            default:
+                size = 72;
+        }
+
+        Bitmap bitmap = Bitmap.createScaledBitmap(BitmapFactory.decodeByteArray(raw, 0, raw.length), size, size, true);
+        intent.putExtra(Intent.EXTRA_SHORTCUT_ICON, bitmap);
+
+        // Now, return the result to the launcher
+        setResult(RESULT_OK, intent);
+        mDb.close();
+        mCursor.close();
+        finish();
+    }
+}
--- a/embedding/android/Makefile.in
+++ b/embedding/android/Makefile.in
@@ -54,16 +54,17 @@ JAVAFILES = \
   GeckoInputConnection.java \
   AlertNotification.java \
   $(NULL)
 
 PROCESSEDJAVAFILES = \
   App.java \
   Restarter.java \
   NotificationHandler.java \
+  LauncherShortcuts.java \
   $(NULL)
 
 
 ifneq (,$(findstring -march=armv7,$(OS_CFLAGS)))
 MIN_CPU_VERSION=7
 else
 MIN_CPU_VERSION=5
 endif
@@ -111,16 +112,18 @@ ICON_PATH = $(topsrcdir)/$(MOZ_BRANDING_
 ICON_PATH_HDPI = $(topsrcdir)/$(MOZ_BRANDING_DIRECTORY)/content/icon64.png
 DEFINES += -DMOZ_ANDROID_SHARED_ID="$(ANDROID_PACKAGE_NAME).sharedID"
 endif
 
 RES_LAYOUT = \
   res/layout/notification_progress.xml \
   res/layout/notification_progress_text.xml \
   res/layout/notification_icon_text.xml \
+  res/layout/launch_app_list.xml \
+  res/layout/launch_app_listitem.xml \
   $(NULL)
 
 RES_VALUES = res/values/colors.xml res/values/themes.xml
 
 AB_rCD = $(shell echo $(AB_CD) | sed -e s/-/-r/)
 
 JAVA_CLASSPATH = $(ANDROID_SDK)/android.jar
 
--- a/embedding/android/locales/en-US/android_strings.dtd
+++ b/embedding/android/locales/en-US/android_strings.dtd
@@ -11,8 +11,11 @@
 <!ENTITY  crash_help_message "Please help us fix this problem!">
 <!ENTITY  crash_send_report_message "Send Mozilla a crash report">
 <!ENTITY  crash_include_url "Include page address">
 <!ENTITY  crash_close_label "Close">
 <!ENTITY  crash_restart_label "Restart &brandShortName;">
 <!ENTITY  sending_crash_report "Sending crash report\u2026">
 <!ENTITY  exit_label "Exit">
 <!ENTITY  continue_label "Continue">
+
+<!ENTITY  launcher_shortcuts_title "&brandShortName; Web Apps">
+<!ENTITY  launcher_shortcuts_empty "No web apps were found">
new file mode 100644
--- /dev/null
+++ b/embedding/android/resources/layout/launch_app_list.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+         android:layout_width="match_parent"
+         android:layout_height="match_parent"
+         android:padding="3dip"
+         android:orientation="vertical"
+         android:windowIsFloating="true">
+
+    <ListView android:id="@android:id/list"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent"
+              android:layout_weight="1"
+              android:drawSelectorOnTop="false"/>
+
+    <TextView android:id="@android:id/empty"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent"
+              android:gravity="center"
+              android:text="@string/launcher_shortcuts_empty"/>
+ </LinearLayout>
new file mode 100644
--- /dev/null
+++ b/embedding/android/resources/layout/launch_app_listitem.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="?android:attr/listPreferredItemHeight"
+    android:padding="6dip"
+    android:orientation="horizontal">
+    <ImageView
+        android:id="@+id/favicon"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:layout_marginRight="6dip"/>
+    <TextView
+        android:id="@+id/title"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:gravity="center_vertical"/>
+ </LinearLayout>
--- a/embedding/android/strings.xml.in
+++ b/embedding/android/strings.xml.in
@@ -16,9 +16,12 @@
   <string name="crash_help_message">&crash_help_message;</string>
   <string name="crash_send_report_message">&crash_send_report_message;</string>
   <string name="crash_include_url">&crash_include_url;</string>
   <string name="crash_close_label">&crash_close_label;</string>
   <string name="crash_restart_label">&crash_restart_label;</string>
   <string name="sending_crash_report">&sending_crash_report;</string>
   <string name="exit_label">&exit_label;</string>
   <string name="continue_label">&continue_label;</string>
+
+  <string name="launcher_shortcuts_title">&launcher_shortcuts_title;</string>
+  <string name="launcher_shortcuts_empty">&launcher_shortcuts_empty;</string>
 </resources>