From 4a65b0c3022357d2b14db196224931cc35f14417 Mon Sep 17 00:00:00 2001 From: Matt Garnes Date: Fri, 20 Jun 2014 13:39:50 -0700 Subject: Add support for CMHome (1/2) Add permission for DashClock extensions. Change Trebuchet to allow viewing additional screen. Add setting that to configure Google Now / CMHome. Change-Id: I63286a2cce87455ed411bd4c77680200eec89be7 --- AndroidManifest.xml | 8 +- res/values/cm_strings.xml | 7 + res/values/preferences_defaults.xml | 1 - src/com/android/launcher/home/Home.java | 17 +- src/com/android/launcher3/Launcher.java | 79 ++++- .../android/launcher3/OverviewSettingsPanel.java | 28 +- src/com/android/launcher3/Workspace.java | 14 +- .../list/SettingsPinnedHeaderAdapter.java | 74 +++-- .../launcher3/settings/SettingsProvider.java | 2 +- .../cyanogenmod/trebuchet/CustomHomeLauncher.java | 349 +++++++++++++++++++++ .../cyanogenmod/trebuchet/TrebuchetLauncher.java | 331 ------------------- .../cyanogenmod/trebuchet/home/HomeWrapper.java | 32 +- 12 files changed, 537 insertions(+), 405 deletions(-) create mode 100644 src/org/cyanogenmod/trebuchet/CustomHomeLauncher.java delete mode 100644 src/org/cyanogenmod/trebuchet/TrebuchetLauncher.java diff --git a/AndroidManifest.xml b/AndroidManifest.xml index dd1761512..830e2c4b4 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -52,7 +52,13 @@ android:name="com.android.launcher3.permission.RECEIVE_LAUNCH_BROADCASTS" android:protectionLevel="signature" /> + + @@ -78,7 +84,7 @@ android:requiredForAllUsers="true" android:supportsRtl="true"> ON OFF + + Google Now + CM Home + Themes @@ -88,4 +92,7 @@ Confirm + + Allows requesting DashClock extension data + Request DashClock extension data diff --git a/res/values/preferences_defaults.xml b/res/values/preferences_defaults.xml index 64d904a0f..a3b835dc6 100644 --- a/res/values/preferences_defaults.xml +++ b/res/values/preferences_defaults.xml @@ -1,7 +1,6 @@ true - false none true @bool/config_workspaceDefaultShowOutlines diff --git a/src/com/android/launcher/home/Home.java b/src/com/android/launcher/home/Home.java index 5dce71e86..e6fedc8db 100644 --- a/src/com/android/launcher/home/Home.java +++ b/src/com/android/launcher/home/Home.java @@ -65,7 +65,7 @@ public interface Home { *
* DO NOT MODIFY! */ - public static final String SIGNATURE = "5/A6Mxkz8gHHzzVf4qZR+hiSOAw="; + public static final String SIGNATURE = "sZFp8JclUBYdIw0QaJZDosZ8SWM="; /** * Defines the name of the metadata used to declared the full qualified Home stub class @@ -97,6 +97,14 @@ public interface Home { public static final int MODE_SEARCH_TEXT = 0x0000; public static final int MODE_SEARCH_VOICE = 0x0001; + /** + * Invoked when creating the Home object to set + * a reference to the host Activity that will + * contain this instance. + * @param context The Activity Context of the host activity. + */ + void setHostActivityContext(Context context); + /** * Invoked the first time the Home app is created.
* This method should be used by implementors classes of this protocol to load the needed @@ -105,6 +113,13 @@ public interface Home { */ void onStart(Context context); + /** + * Load and show the content of this home app if true, + * hide and remove providers if false. + * @param showContent Should content be shown + */ + void setShowContent(boolean showContent); + /** * Invoked when the Home app should be destroy.
* This method should be used by implementors classes of this protocol to unload all unneeded diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index fac0b8835..fbb3189f9 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -56,7 +56,6 @@ import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Point; import android.graphics.PorterDuff; -import android.graphics.PorterDuffColorFilter; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.net.Uri; @@ -243,8 +242,6 @@ public class Launcher extends Activity private static int NEW_APPS_ANIMATION_INACTIVE_TIMEOUT_SECONDS = 5; private static int NEW_APPS_ANIMATION_DELAY = 500; - private boolean mGelIntegrationEnabled = false; - private final BroadcastReceiver mCloseSystemDialogsReceiver = new CloseSystemDialogsIntentReceiver(); private final ContentObserver mWidgetObserver = new AppWidgetResetObserver(); @@ -380,6 +377,34 @@ public class Launcher extends Activity private BubbleTextView mWaitingForResume; + public enum CustomContentMode { + DISABLED(0), + GEL(1), + CUSTOM_HOME(2); + + private final int mValue; + private CustomContentMode(int value) { + mValue = value; + } + + public int getValue() { + return mValue; + } + + public static CustomContentMode getModeForValue(int value) { + switch (value) { + case 0: + return DISABLED; + case 1: + return GEL; + default : + return CUSTOM_HOME; + } + } + } + + private CustomContentMode mCustomContentMode = CustomContentMode.CUSTOM_HOME; + // Preferences private boolean mHideIconLabels; @@ -492,7 +517,7 @@ public class Launcher extends Activity mSavedState = savedInstanceState; restoreState(mSavedState); - restoreGelSetting(); + restoreCustomContentMode(); if (PROFILE_STARTUP) { android.os.Debug.stopMethodTracing(); @@ -541,10 +566,11 @@ public class Launcher extends Activity "cyanogenmod.permission.PROTECTED_APP", null); } - public void restoreGelSetting() { - mGelIntegrationEnabled = SettingsProvider.getBoolean(this, - SettingsProvider.SETTINGS_UI_HOMESCREEN_SEARCH_SCREEN_LEFT, - R.bool.preferences_interface_homescreen_search_screen_left_default); + public void restoreCustomContentMode() { + mCustomContentMode = CustomContentMode.getModeForValue( + SettingsProvider.getIntCustomDefault(this, + SettingsProvider.SETTINGS_UI_HOMESCREEN_SEARCH_PANEL_LEFT, + CustomContentMode.DISABLED.getValue())); } void initializeDynamicGrid() { @@ -555,7 +581,7 @@ public class Launcher extends Activity SettingsProvider.SETTINGS_UI_HOMESCREEN_HIDE_ICON_LABELS, R.bool.preferences_interface_homescreen_hide_icon_labels_default); - restoreGelSetting(); + restoreCustomContentMode(); // Determine the dynamic grid properties Point smallestSize = new Point(); @@ -589,7 +615,23 @@ public class Launcher extends Activity } protected boolean hasCustomContentToLeft() { - return isGelIntegrationSupported() && isGelIntegrationEnabled(); + switch(getCustomContentMode()) { + case GEL: + return isGelIntegrationSupported(); + case CUSTOM_HOME: + return isCustomHomeActive(); + default: + return false; + } + } + + /** + * Returns true if the custom home application is initialized and ready + * for the user to scroll to it. To be implemented by subclasses. + * @return True if the custom home view is initialized. + */ + protected boolean isCustomHomeActive() { + return false; } public boolean isGelIntegrationSupported() { @@ -602,12 +644,16 @@ public class Launcher extends Activity return globalSearchActivity != null && isCM(); } - public boolean isGelIntegrationEnabled() { - return mGelIntegrationEnabled; + public CustomContentMode getCustomContentMode() { + return mCustomContentMode; + } + + public void setCustomContentMode(CustomContentMode customContentMode) { + mCustomContentMode = customContentMode; } public void onCustomContentLaunch() { - if(isGelIntegrationEnabled() && isGelIntegrationSupported()) { + if(isCustomContentModeGel() && isGelIntegrationSupported()) { GelIntegrationHelper.getInstance().registerSwipeBackGestureListenerAndStartGel(this, mWorkspace.isLayoutRtl()); } } @@ -1060,10 +1106,9 @@ public class Launcher extends Activity } super.onResume(); - updateGridIfNeeded(); - if(isGelIntegrationEnabled() && isGelIntegrationSupported()) { + if(isCustomContentModeGel() && isGelIntegrationSupported()) { GelIntegrationHelper.getInstance().handleGelResume(); } @@ -1221,6 +1266,10 @@ public class Launcher extends Activity } } + protected boolean isCustomContentModeGel() { + return mCustomContentMode == CustomContentMode.GEL; + } + public interface CustomContentCallbacks { // Custom content is completely shown public void onShow(); diff --git a/src/com/android/launcher3/OverviewSettingsPanel.java b/src/com/android/launcher3/OverviewSettingsPanel.java index 9c0e4b453..e14b78942 100644 --- a/src/com/android/launcher3/OverviewSettingsPanel.java +++ b/src/com/android/launcher3/OverviewSettingsPanel.java @@ -43,25 +43,13 @@ public class OverviewSettingsPanel { res.getString(R.string.drawer_settings), res.getString(R.string.app_settings)}; - String[] values; - if(mLauncher.isGelIntegrationSupported()) { - values = new String[]{ - res.getString(R.string.home_screen_search_text), - res.getString(R.string.search_screen_left_text), - res.getString(R.string.scroll_effect_text), - res.getString(R.string.icon_labels), - res.getString(R.string.scrolling_wallpaper), - res.getString(R.string.grid_size_text)}; - } else { - values = new String[]{ - res.getString(R.string.home_screen_search_text), - res.getString(R.string.scroll_effect_text), - res.getString(R.string.icon_labels), - res.getString(R.string.scrolling_wallpaper), - res.getString(R.string.grid_size_text)}; - } - - mValues = values; + mValues = new String[]{ + res.getString(R.string.home_screen_search_text), + res.getString(R.string.search_screen_left_text), + res.getString(R.string.scroll_effect_text), + res.getString(R.string.icon_labels), + res.getString(R.string.scrolling_wallpaper), + res.getString(R.string.grid_size_text)}; String[] valuesDrawer = new String[] { res.getString(R.string.scroll_effect_text), @@ -80,7 +68,7 @@ public class OverviewSettingsPanel { mSettingsAdapter.addPartition(false, true); mSettingsAdapter.mPinnedHeaderCount = headers.length; - mSettingsAdapter.changeCursor(0, createCursor(headers[0], values)); + mSettingsAdapter.changeCursor(0, createCursor(headers[0], mValues)); mSettingsAdapter.changeCursor(1, createCursor(headers[1], valuesDrawer)); mSettingsAdapter.changeCursor(2, createCursor(headers[2], valuesApp)); mListView.setAdapter(mSettingsAdapter); diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 63e893da1..7501dbf7d 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -1309,8 +1309,6 @@ public class Workspace extends SmoothPagedView int customPageIndex = getPageIndexForScreenId(CUSTOM_CONTENT_SCREEN_ID); if (hasCustomContent() && whichPage == customPageIndex && !mCustomContentShowing) { if(!isInOverviewMode()) { - mCustomContentShowing = true; - // Start Google Now and register the gesture to return to Trebuchet mLauncher.onCustomContentLaunch(); } } @@ -1833,10 +1831,14 @@ public class Workspace extends SmoothPagedView int customPageIndex = getPageIndexForScreenId(CUSTOM_CONTENT_SCREEN_ID); // mCustomContentShowing can be lost if the Activity is recreated, // So make sure it is set to the right value. + boolean restoreCustomContentShowing = ((customPageIndex == getCurrentPage()) + || (customPageIndex == getNextPage())) + && hasCustomContent(); mCustomContentShowing = mCustomContentShowing - || (customPageIndex == getCurrentPage() - && hasCustomContent()); - if (mCustomContentShowing && mLauncher.isGelIntegrationEnabled()) { + || restoreCustomContentShowing; + if (mCustomContentShowing + && (mLauncher.getCustomContentMode() == Launcher.CustomContentMode.GEL) + && !isInOverviewMode()) { moveToScreen((customPageIndex + 1), true); } } @@ -4931,7 +4933,7 @@ public class Workspace extends SmoothPagedView int idx = getPageIndexForScreenId(mDefaultScreenId); int ccIndex = getPageIndexForScreenId(CUSTOM_CONTENT_SCREEN_ID); if(hasCustomContent() && (idx == ccIndex || idx == -1) - && mLauncher.isGelIntegrationEnabled()) { + && !isInOverviewMode()) { idx = 1; } moveToScreen(idx, animate); diff --git a/src/com/android/launcher3/list/SettingsPinnedHeaderAdapter.java b/src/com/android/launcher3/list/SettingsPinnedHeaderAdapter.java index 7fb15fc12..a4a68fb85 100644 --- a/src/com/android/launcher3/list/SettingsPinnedHeaderAdapter.java +++ b/src/com/android/launcher3/list/SettingsPinnedHeaderAdapter.java @@ -124,15 +124,7 @@ public class SettingsPinnedHeaderAdapter extends PinnedHeaderListAdapter { ((TextView) v.findViewById(R.id.item_state)).setText(state); } else if (title.equals(res .getString(R.string.search_screen_left_text))) { - boolean current = SettingsProvider - .getBoolean( - mContext, - SettingsProvider.SETTINGS_UI_HOMESCREEN_SEARCH_SCREEN_LEFT, - R.bool.preferences_interface_homescreen_search_screen_left_default); - String state = current ? res.getString( - R.string.setting_state_on) : res.getString( - R.string.setting_state_off); - ((TextView) v.findViewById(R.id.item_state)).setText(state); + updateSearchPanelItem(v); } else if (title.equals(res.getString(R.string.scrolling_wallpaper))) { boolean current = SettingsProvider .getBoolean( @@ -186,6 +178,24 @@ public class SettingsPinnedHeaderAdapter extends PinnedHeaderListAdapter { ((TextView) v.findViewById(R.id.item_state)).setText(state); } + public void updateSearchPanelItem(View v) { + String state = ""; + switch (mLauncher.getCustomContentMode()) { + case DISABLED: + state = mLauncher.getResources().getString( + R.string.setting_state_off); + break; + case GEL: + state = mLauncher.getResources().getString(R.string.search_panel_gel); + break; + default: + state = mLauncher.getResources().getString( + R.string.search_panel_custom_home); + break; + } + ((TextView) v.findViewById(R.id.item_state)).setText(state); + } + public void updateDynamicGridSizeSettingsItem(View v) { DeviceProfile.GridSize gridSize = DeviceProfile.GridSize.getModeForValue( SettingsProvider.getIntCustomDefault(mLauncher, @@ -293,25 +303,11 @@ public class SettingsPinnedHeaderAdapter extends PinnedHeaderListAdapter { } else if (value.equals(res .getString(R.string.search_screen_left_text)) && ((Integer)v.getTag() == OverviewSettingsPanel.HOME_SETTINGS_POSITION)) { - - boolean current = SettingsProvider.getBoolean(mContext, - SettingsProvider.SETTINGS_UI_HOMESCREEN_SEARCH_SCREEN_LEFT, - R.bool.preferences_interface_homescreen_search_screen_left_default); - - // If GEL integration is not supported, do not allow the user to turn it on. - if(!current && !mLauncher.isGelIntegrationSupported()) { - Toast.makeText(mLauncher.getApplicationContext(), - res.getString(R.string.search_screen_left_unsupported_toast), - Toast.LENGTH_SHORT).show(); - } else { - onSettingsBooleanChanged( - v, - SettingsProvider.SETTINGS_UI_HOMESCREEN_SEARCH_SCREEN_LEFT, - R.bool.preferences_interface_homescreen_search_screen_left_default); - mLauncher.restoreGelSetting(); - mLauncher.getWorkspace().updatePageScrollForCustomPage(!current); - mLauncher.setUpdateDynamicGrid(); - } + onClickSearchPanelButton(); + boolean customContentEnabled = + mLauncher.getCustomContentMode() != Launcher.CustomContentMode.DISABLED; + mLauncher.getWorkspace().updatePageScrollForCustomPage(customContentEnabled); + mLauncher.setUpdateDynamicGrid(); } else if (value.equals(res .getString(R.string.grid_size_text))) { mLauncher.onClickDynamicGridSizeButton(); @@ -370,4 +366,26 @@ public class SettingsPinnedHeaderAdapter extends PinnedHeaderListAdapter { notifyDataSetChanged(); } + + private void onClickSearchPanelButton() { + int searchPanelVal = SettingsProvider.getIntCustomDefault(mLauncher, + SettingsProvider.SETTINGS_UI_HOMESCREEN_SEARCH_PANEL_LEFT, + Launcher.CustomContentMode.DISABLED.getValue()); + + Launcher.CustomContentMode nextCCMode = + Launcher.CustomContentMode.getModeForValue(searchPanelVal + 1); + if(nextCCMode == Launcher.CustomContentMode.GEL && !mLauncher.isGelIntegrationSupported()) { + // GEL is not supported, skip that option + searchPanelVal++; + } + + searchPanelVal = (searchPanelVal + 1) % Launcher.CustomContentMode.values().length; + mLauncher.setCustomContentMode(Launcher.CustomContentMode.getModeForValue(searchPanelVal)); + + SettingsProvider.putInt(mLauncher, + SettingsProvider.SETTINGS_UI_HOMESCREEN_SEARCH_PANEL_LEFT, + searchPanelVal); + + notifyDataSetChanged(); + } } diff --git a/src/com/android/launcher3/settings/SettingsProvider.java b/src/com/android/launcher3/settings/SettingsProvider.java index 32329ab83..8cde92555 100644 --- a/src/com/android/launcher3/settings/SettingsProvider.java +++ b/src/com/android/launcher3/settings/SettingsProvider.java @@ -26,7 +26,7 @@ public final class SettingsProvider { public static final String SETTINGS_UI_HOMESCREEN_DEFAULT_SCREEN_ID = "ui_homescreen_default_screen_id"; public static final String SETTINGS_UI_HOMESCREEN_SEARCH = "ui_homescreen_search"; - public static final String SETTINGS_UI_HOMESCREEN_SEARCH_SCREEN_LEFT = "ui_homescreen_search_screen_left"; + public static final String SETTINGS_UI_HOMESCREEN_SEARCH_PANEL_LEFT = "ui_homescreen_search_panel_left"; public static final String SETTINGS_UI_HOMESCREEN_HIDE_ICON_LABELS = "ui_homescreen_general_hide_icon_labels"; public static final String SETTINGS_UI_HOMESCREEN_SCROLLING_TRANSITION_EFFECT = "ui_homescreen_scrolling_transition_effect"; public static final String SETTINGS_UI_HOMESCREEN_SCROLLING_WALLPAPER_SCROLL = "ui_homescreen_scrolling_wallpaper_scroll"; diff --git a/src/org/cyanogenmod/trebuchet/CustomHomeLauncher.java b/src/org/cyanogenmod/trebuchet/CustomHomeLauncher.java new file mode 100644 index 000000000..47c9c3b2c --- /dev/null +++ b/src/org/cyanogenmod/trebuchet/CustomHomeLauncher.java @@ -0,0 +1,349 @@ +/* + * Copyright (C) 2014 The CyanogenMod 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 org.cyanogenmod.trebuchet; + +import android.content.BroadcastReceiver; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.res.Resources; +import android.graphics.Color; +import android.graphics.PorterDuff; +import android.graphics.drawable.Drawable; +import android.os.Bundle; +import android.util.Log; +import android.util.SparseArray; +import android.view.animation.AccelerateInterpolator; +import android.widget.ImageView; + +import com.android.launcher.home.Home; +import com.android.launcher3.Launcher; +import com.android.launcher3.R; + +import org.cyanogenmod.trebuchet.home.HomeUtils; +import org.cyanogenmod.trebuchet.home.HomeWrapper; + +import java.lang.Override; + +public class CustomHomeLauncher extends Launcher { + + private static final String TAG = "CustomHomeLauncher"; + + private static final boolean DEBUG = false; + private static final float MIN_PROGRESS = 0; + private static final float MAX_PROGRESS = 1; + + + private static class HomeAppStub { + private final int mUid; + private final ComponentName mComponentName; + private final HomeWrapper mInstance; + + private HomeAppStub(int uid, ComponentName componentName, + Context context, Context homeActivityContext) + throws SecurityException, ReflectiveOperationException { + super(); + mUid = uid; + mComponentName = componentName; + + // Load a new instance of the Home app + ClassLoader classloader = context.getClassLoader(); + Class homeInterface = classloader.loadClass(Home.class.getName()); + Class homeClazz = classloader.loadClass(mComponentName.getClassName()); + mInstance = new HomeWrapper(context, homeInterface, + homeClazz.newInstance(), homeActivityContext); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((mComponentName == null) ? 0 : mComponentName.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + HomeAppStub other = (HomeAppStub) obj; + if (mComponentName == null) { + if (other.mComponentName != null) + return false; + } else if (!mComponentName.equals(other.mComponentName)) + return false; + return true; + } + } + + private BroadcastReceiver mPackageReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + // Obtain the current instance or a new one if the current instance not exists + boolean invalidate = false; + String action = intent.getAction(); + if (action.equals(Intent.ACTION_PACKAGE_CHANGED) || + action.equals(Intent.ACTION_PACKAGE_REPLACED) || + action.equals(Intent.ACTION_PACKAGE_RESTARTED)) { + if (mCurrentHomeApp != null && intent.getIntExtra(Intent.EXTRA_UID, -1) + == mCurrentHomeApp.mUid) { + // The current Home app has changed or restarted. Invalidate the current + // one to be sure we will get all the new changes (if any) + if (DEBUG) Log.d(TAG, "Home package has changed. Invalidate layout."); + invalidate = true; + } + } + obtainCurrentHomeAppStubLocked(invalidate); + } + }; + + private CustomContentCallbacks mCustomContentCallbacks = new CustomContentCallbacks() { + @Override + public void onShow() { + if (mCurrentHomeApp != null) { + mCurrentHomeApp.mInstance.onShow(); + } + } + + @Override + public void onScrollProgressChanged(float progress) { + updateQsbBarColorState(progress); + if (mCurrentHomeApp != null) { + mCurrentHomeApp.mInstance.onScrollProgressChanged(progress); + } + } + + @Override + public void onHide() { + if (mCurrentHomeApp != null) { + mCurrentHomeApp.mInstance.onHide(); + } + } + }; + + private HomeAppStub mCurrentHomeApp; + private AccelerateInterpolator mQSBAlphaInterpolator; + + private QSBScroller mQsbScroller; + private int mQsbInitialAlphaState; + private int mQsbEndAlphaState; + private int mQsbButtonsEndColorFilter; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + mQSBAlphaInterpolator = new AccelerateInterpolator(); + + // Set QsbBar color state + final Resources res = getResources(); + mQsbInitialAlphaState = res.getInteger(R.integer.qsb_initial_alpha_state); + mQsbEndAlphaState = res.getInteger(R.integer.qsb_end_alpha_state); + mQsbButtonsEndColorFilter = res.getInteger(R.integer.qsb_buttons_end_colorfilter); + updateQsbBarColorState(MIN_PROGRESS); + + // Obtain the user-defined Home app or a valid one + obtainCurrentHomeAppStubLocked(true); + + // Register this class to listen for new/deleted packages + IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED); + filter.addAction(Intent.ACTION_PACKAGE_REMOVED); + filter.addAction(Intent.ACTION_PACKAGE_FULLY_REMOVED); + filter.addAction(Intent.ACTION_PACKAGE_CHANGED); + filter.addAction(Intent.ACTION_PACKAGE_REPLACED); + filter.addAction(Intent.ACTION_PACKAGE_RESTARTED); + filter.addDataScheme("package"); + registerReceiver(mPackageReceiver, filter); + } + + @Override + public void onDestroy() { + super.onDestroy(); + + // Unregister services + unregisterReceiver(mPackageReceiver); + } + + @Override + protected void onResume() { + if (mCurrentHomeApp != null) { + mCurrentHomeApp.mInstance.onResume(); + } + super.onResume(); + } + + @Override + protected void onPause() { + if (mCurrentHomeApp != null) { + mCurrentHomeApp.mInstance.onPause(); + } + super.onPause(); + } + + @Override + protected boolean isCustomHomeActive() { + return mCurrentHomeApp != null; + } + + @Override + protected void invalidateHasCustomContentToLeft() { + invalidateHomeStub(); + super.invalidateHasCustomContentToLeft(); + } + + @Override + protected void populateCustomContentContainer() { + if (mCurrentHomeApp != null) { + mQsbScroller = addToCustomContentPage(mCurrentHomeApp.mInstance.createCustomView(), + mCustomContentCallbacks, mCurrentHomeApp.mInstance.getName()); + + if (!isCustomContentModeGel()) { + mCurrentHomeApp.mInstance.setShowContent(true); + } + } + } + + @Override + protected boolean hasCustomSearchSupport() { + return hasCustomContentToLeft() && mCurrentHomeApp.mInstance.isOperationSupported( + Home.FLAG_OP_CUSTOM_SEARCH); + } + + @Override + protected void requestSearch(int mode) { + if (!hasCustomSearchSupport()) { + return; + } + mCurrentHomeApp.mInstance.onRequestSearch(mode); + } + + @Override + public void updateDynamicGrid() { + super.updateDynamicGrid(); + + if (isCustomContentModeGel() && mCurrentHomeApp != null) { + mCurrentHomeApp.mInstance.setShowContent(false); + } else if (getCustomContentMode() == CustomContentMode.CUSTOM_HOME + && mCurrentHomeApp != null) { + mCurrentHomeApp.mInstance.setShowContent(true); + } + } + + private synchronized void obtainCurrentHomeAppStubLocked(boolean invalidate) { + if (DEBUG) Log.d(TAG, "obtainCurrentHomeAppStubLocked called (" + invalidate + ")"); + + SparseArray packages = HomeUtils.getInstalledHomePackages(this); + if (!invalidate && mCurrentHomeApp != null && + packages.get(mCurrentHomeApp.mUid) != null) { + // We still have a valid Home app + return; + } + + // We don't have a valid Home app, so we need to destroy the current the custom content + destroyHomeStub(); + + // Return the default valid home app + int size = packages.size(); + for (int i = 0; i < size; i++) { + int key = packages.keyAt(i); + ComponentName pkg = packages.get(key); + String qualifiedPkg = pkg.toShortString(); + Context ctx = HomeUtils.createNewHomePackageContext(this, pkg); + if (ctx == null) { + // We failed to create a valid context. Will try with the next package + continue; + } + try { + mCurrentHomeApp = new HomeAppStub(key, pkg, ctx, this); + } catch (ReflectiveOperationException ex) { + if (!DEBUG) { + Log.w(TAG, "Cannot instantiate home package: " + qualifiedPkg + ". Ignored."); + } else { + Log.w(TAG, "Cannot instantiate home package: " + qualifiedPkg + + ". Ignored.", ex); + } + } catch (SecurityException ex) { + if (!DEBUG) { + Log.w(TAG, "Home package is insecure: " + qualifiedPkg + ". Ignored."); + } else { + Log.w(TAG, "Home package is insecure: " + qualifiedPkg + ". Ignored.", ex); + } + } + + // Notify home app that is going to be used + if (mCurrentHomeApp != null) { + mCurrentHomeApp.mInstance.onStart(); + } + } + + // Don't have a valid package. Anyway notify the launcher that custom content has changed + invalidateHasCustomContentToLeft(); + } + + private void invalidateHomeStub() { + if (mCurrentHomeApp != null) { + mCurrentHomeApp.mInstance.onInvalidate(); + if (DEBUG) Log.d(TAG, "Home package " + mCurrentHomeApp.mComponentName.toShortString() + + " was invalidated."); + } + } + + private void destroyHomeStub() { + if (mCurrentHomeApp != null) { + mCurrentHomeApp.mInstance.onInvalidate(); + mCurrentHomeApp.mInstance.onDestroy(); + if (DEBUG) Log.d(TAG, "Home package " + mCurrentHomeApp.mComponentName.toShortString() + + " was destroyed."); + } + mQsbScroller = null; + mCurrentHomeApp = null; + } + + private void updateQsbBarColorState(float progress) { + if (getQsbBar() != null) { + float interpolation = mQSBAlphaInterpolator.getInterpolation(progress); + + // Background alpha + int alphaInterpolation = (int)(mQsbInitialAlphaState + + (interpolation * (mQsbEndAlphaState - mQsbInitialAlphaState))); + Drawable background = getQsbBar().getBackground(); + if (background != null) { + background.setAlpha(alphaInterpolation); + } + + // Buttons color filter + int colorInterpolation = (int)(255 - (interpolation * mQsbButtonsEndColorFilter)); + int color = Color.rgb(colorInterpolation, colorInterpolation,colorInterpolation); + ImageView voiceButton = getQsbBarVoiceButton(); + if (voiceButton != null) { + if (progress > 0) { + voiceButton.setColorFilter(color, PorterDuff.Mode.SRC_IN); + } + } + ImageView searchButton = getQsbBarSearchButton(); + if (searchButton != null) { + if (progress > 0) { + searchButton.setColorFilter(color, PorterDuff.Mode.SRC_IN); + } + } + } + } +} diff --git a/src/org/cyanogenmod/trebuchet/TrebuchetLauncher.java b/src/org/cyanogenmod/trebuchet/TrebuchetLauncher.java deleted file mode 100644 index 3c5e6a70b..000000000 --- a/src/org/cyanogenmod/trebuchet/TrebuchetLauncher.java +++ /dev/null @@ -1,331 +0,0 @@ -/* - * Copyright (C) 2014 The CyanogenMod 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 org.cyanogenmod.trebuchet; - -import android.content.BroadcastReceiver; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.res.Resources; -import android.graphics.Color; -import android.graphics.PorterDuff; -import android.graphics.drawable.Drawable; -import android.os.Bundle; -import android.util.Log; -import android.util.SparseArray; -import android.view.animation.AccelerateInterpolator; -import android.widget.ImageView; - -import com.android.launcher.home.Home; -import com.android.launcher3.Launcher; -import com.android.launcher3.R; - -import org.cyanogenmod.trebuchet.home.HomeUtils; -import org.cyanogenmod.trebuchet.home.HomeWrapper; - -import java.lang.Override; - -public class TrebuchetLauncher extends Launcher { - - private static final String TAG = "TrebuchetLauncher"; - - private static final boolean DEBUG = false; - private static final float MIN_PROGRESS = 0; - private static final float MAX_PROGRESS = 1; - - - private static class HomeAppStub { - private final int mUid; - private final ComponentName mComponentName; - private final HomeWrapper mInstance; - - private HomeAppStub(int uid, ComponentName componentName, Context context) - throws SecurityException, ReflectiveOperationException { - super(); - mUid = uid; - mComponentName = componentName; - - // Load a new instance of the Home app - ClassLoader classloader = context.getClassLoader(); - Class homeInterface = classloader.loadClass(Home.class.getName()); - Class homeClazz = classloader.loadClass(mComponentName.getClassName()); - mInstance = new HomeWrapper(context, homeInterface, homeClazz.newInstance()); - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((mComponentName == null) ? 0 : mComponentName.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - HomeAppStub other = (HomeAppStub) obj; - if (mComponentName == null) { - if (other.mComponentName != null) - return false; - } else if (!mComponentName.equals(other.mComponentName)) - return false; - return true; - } - } - - private BroadcastReceiver mPackageReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - // Obtain the current instance or a new one if the current instance not exists - boolean invalidate = false; - String action = intent.getAction(); - if (action.equals(Intent.ACTION_PACKAGE_CHANGED) || - action.equals(Intent.ACTION_PACKAGE_REPLACED) || - action.equals(Intent.ACTION_PACKAGE_RESTARTED)) { - if (mCurrentHomeApp != null && intent.getIntExtra(Intent.EXTRA_UID, -1) - == mCurrentHomeApp.mUid) { - // The current Home app has changed or restarted. Invalidate the current - // one to be sure we will get all the new changes (if any) - if (DEBUG) Log.d(TAG, "Home package has changed. Invalidate layout."); - invalidate = true; - } - } - obtainCurrentHomeAppStubLocked(invalidate); - } - }; - - private CustomContentCallbacks mCustomContentCallbacks = new CustomContentCallbacks() { - @Override - public void onShow() { - if (mCurrentHomeApp != null) { - mCurrentHomeApp.mInstance.onShow(); - } - } - - @Override - public void onScrollProgressChanged(float progress) { - updateQsbBarColorState(progress); - if (mCurrentHomeApp != null) { - mCurrentHomeApp.mInstance.onScrollProgressChanged(progress); - } - } - - @Override - public void onHide() { - if (mCurrentHomeApp != null) { - mCurrentHomeApp.mInstance.onHide(); - } - } - }; - - private HomeAppStub mCurrentHomeApp; - private AccelerateInterpolator mQSBAlphaInterpolator; - - private QSBScroller mQsbScroller; - private int mQsbInitialAlphaState; - private int mQsbEndAlphaState; - private int mQsbButtonsEndColorFilter; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - mQSBAlphaInterpolator = new AccelerateInterpolator(); - - // Set QsbBar color state - final Resources res = getResources(); - mQsbInitialAlphaState = res.getInteger(R.integer.qsb_initial_alpha_state); - mQsbEndAlphaState = res.getInteger(R.integer.qsb_end_alpha_state); - mQsbButtonsEndColorFilter = res.getInteger(R.integer.qsb_buttons_end_colorfilter); - updateQsbBarColorState(MIN_PROGRESS); - - // Obtain the user-defined Home app or a valid one - obtainCurrentHomeAppStubLocked(true); - - // Register this class to listen for new/deleted packages - IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED); - filter.addAction(Intent.ACTION_PACKAGE_REMOVED); - filter.addAction(Intent.ACTION_PACKAGE_FULLY_REMOVED); - filter.addAction(Intent.ACTION_PACKAGE_CHANGED); - filter.addAction(Intent.ACTION_PACKAGE_REPLACED); - filter.addAction(Intent.ACTION_PACKAGE_RESTARTED); - filter.addDataScheme("package"); - registerReceiver(mPackageReceiver, filter); - } - - @Override - public void onDestroy() { - super.onDestroy(); - - // Unregister services - unregisterReceiver(mPackageReceiver); - } - - @Override - protected void onResume() { - if (mCurrentHomeApp != null) { - mCurrentHomeApp.mInstance.onResume(); - } - super.onResume(); - } - - @Override - protected void onPause() { - if (mCurrentHomeApp != null) { - mCurrentHomeApp.mInstance.onPause(); - } - super.onPause(); - } - - @Override - protected boolean hasCustomContentToLeft() { - return mCurrentHomeApp != null && super.hasCustomContentToLeft(); - } - - @Override - protected void invalidateHasCustomContentToLeft() { - invalidateHomeStub(); - super.invalidateHasCustomContentToLeft(); - } - - @Override - protected void populateCustomContentContainer() { - if (mCurrentHomeApp != null) { - mQsbScroller = addToCustomContentPage(mCurrentHomeApp.mInstance.createCustomView(), - mCustomContentCallbacks, mCurrentHomeApp.mInstance.getName()); - } - } - - @Override - protected boolean hasCustomSearchSupport() { - return hasCustomContentToLeft() && mCurrentHomeApp.mInstance.isOperationSupported( - Home.FLAG_OP_CUSTOM_SEARCH); - } - - @Override - protected void requestSearch(int mode) { - if (!hasCustomSearchSupport()) { - return; - } - mCurrentHomeApp.mInstance.onRequestSearch(mode); - } - - private synchronized void obtainCurrentHomeAppStubLocked(boolean invalidate) { - if (DEBUG) Log.d(TAG, "obtainCurrentHomeAppStubLocked called (" + invalidate + ")"); - - SparseArray packages = HomeUtils.getInstalledHomePackages(this); - if (!invalidate && mCurrentHomeApp != null && - packages.get(mCurrentHomeApp.mUid) != null) { - // We still have a valid Home app - return; - } - - // We don't have a valid Home app, so we need to destroy the current the custom content - destroyHomeStub(); - - // Return the default valid home app - int size = packages.size(); - for (int i = 0; i < size; i++) { - int key = packages.keyAt(i); - ComponentName pkg = packages.get(key); - String qualifiedPkg = pkg.toShortString(); - Context ctx = HomeUtils.createNewHomePackageContext(this, pkg); - if (ctx == null) { - // We failed to create a valid context. Will try with the next package - continue; - } - try { - mCurrentHomeApp = new HomeAppStub(key, pkg, ctx); - } catch (ReflectiveOperationException ex) { - if (!DEBUG) { - Log.w(TAG, "Cannot instantiate home package: " + qualifiedPkg + ". Ignored."); - } else { - Log.w(TAG, "Cannot instantiate home package: " + qualifiedPkg + - ". Ignored.", ex); - } - } catch (SecurityException ex) { - if (!DEBUG) { - Log.w(TAG, "Home package is insecure: " + qualifiedPkg + ". Ignored."); - } else { - Log.w(TAG, "Home package is insecure: " + qualifiedPkg + ". Ignored.", ex); - } - } - - // Notify home app that is going to be used - if (mCurrentHomeApp != null) { - mCurrentHomeApp.mInstance.onStart(); - } - } - - // Don't have a valid package. Anyway notify the launcher that custom content has changed - invalidateHasCustomContentToLeft(); - } - - private void invalidateHomeStub() { - if (mCurrentHomeApp != null) { - mCurrentHomeApp.mInstance.onInvalidate(); - if (DEBUG) Log.d(TAG, "Home package " + mCurrentHomeApp.mComponentName.toShortString() - + " was invalidated."); - } - } - - private void destroyHomeStub() { - if (mCurrentHomeApp != null) { - mCurrentHomeApp.mInstance.onInvalidate(); - mCurrentHomeApp.mInstance.onDestroy(); - if (DEBUG) Log.d(TAG, "Home package " + mCurrentHomeApp.mComponentName.toShortString() - + " was destroyed."); - } - mQsbScroller = null; - mCurrentHomeApp = null; - } - - private void updateQsbBarColorState(float progress) { - if (getQsbBar() != null) { - float interpolation = mQSBAlphaInterpolator.getInterpolation(progress); - - // Background alpha - int alphaInterpolation = (int)(mQsbInitialAlphaState + - (interpolation * (mQsbEndAlphaState - mQsbInitialAlphaState))); - Drawable background = getQsbBar().getBackground(); - if (background != null) { - background.setAlpha(alphaInterpolation); - } - - // Buttons color filter - int colorInterpolation = (int)(255 - (interpolation * mQsbButtonsEndColorFilter)); - int color = Color.rgb(colorInterpolation, colorInterpolation,colorInterpolation); - ImageView voiceButton = getQsbBarVoiceButton(); - if (voiceButton != null) { - if (progress > 0) { - voiceButton.setColorFilter(color, PorterDuff.Mode.SRC_IN); - } - } - ImageView searchButton = getQsbBarSearchButton(); - if (searchButton != null) { - if (progress > 0) { - searchButton.setColorFilter(color, PorterDuff.Mode.SRC_IN); - } - } - } - } -} diff --git a/src/org/cyanogenmod/trebuchet/home/HomeWrapper.java b/src/org/cyanogenmod/trebuchet/home/HomeWrapper.java index df8b6cae6..1c7dfda66 100644 --- a/src/org/cyanogenmod/trebuchet/home/HomeWrapper.java +++ b/src/org/cyanogenmod/trebuchet/home/HomeWrapper.java @@ -18,6 +18,7 @@ package org.cyanogenmod.trebuchet.home; import android.content.Context; import android.util.Base64; +import android.util.Log; import android.util.SparseArray; import android.view.View; @@ -45,6 +46,7 @@ public class HomeWrapper { private static final int M_LAST_ID = M_ID_GETOPERATIONFLAGS + 1; private final Context mContext; + private final Context mHostActivityContext; private final Class mClass; private final Object mInstance; @@ -53,12 +55,16 @@ public class HomeWrapper { private final int mNotificationFlags; private final int mOperationFlags; - public HomeWrapper(Context context, Class cls, Object instance) throws SecurityException { + public HomeWrapper(Context context, Class cls, + Object instance, + Context hostActivityContext) throws SecurityException { super(); mContext = context; + mHostActivityContext = hostActivityContext; mClass = cls; mInstance = instance; cachedMethods = new SparseArray(M_LAST_ID); + setHostActivityContext(); final String sha1 = createDigest(cls); if (!sha1.equals(Home.SIGNATURE)) { @@ -71,11 +77,35 @@ public class HomeWrapper { mOperationFlags = getOperationFlags(); } + /** @see Home#setHostActivityContext(Context) **/ + private void setHostActivityContext() { + try { + Method method = mClass.getMethod("setHostActivityContext", Context.class); + method.invoke(mInstance, mHostActivityContext); + } catch (ReflectiveOperationException ex) { + throw new SecurityException(ex); + } + } + /** @see Home#onStart(Context) **/ public void onStart() { invokeVoidContextMethod(M_ID_ONSTART, "onStart"); } + /** + * Load and show the content of this home app if true, + * hide and remove providers if false. + * @param showContent Should content be shown + */ + public void setShowContent(boolean showContent) { + try { + Method method = mClass.getMethod("setShowContent", Context.class, boolean.class); + method.invoke(mInstance, mContext, showContent); + } catch (ReflectiveOperationException ex) { + throw new SecurityException(ex); + } + } + /** @see Home#onDestroy(Context) **/ public void onDestroy() { invokeVoidContextMethod(M_ID_ONDESTROY, "onDestroy"); -- cgit v1.2.3