Android 7.1 Launcher3 支持按键操作显示所有应用

简介: Android 7.1 Launcher3 支持按键操作显示所有应用

平台


Android 7.1 + RK3288


问题


系统支持遥控操作, 但是, 在主界面时, 无法打开所有应用列表.

在主界面, 不管上下左右如何操作, 都无法打开应用列表.


image.pngimage.png


解决方案


与旧版相似, 显示所有应用图标

diff --git a/packages/apps/Launcher3/src_config/com/android/launcher3/config/FeatureFlags.java b/packages/apps/Launcher3/src_config/com/android/launcher3/config/FeatureFlags.java
index 8ee5497..ed35fda 100644
--- a/packages/apps/Launcher3/src_config/com/android/launcher3/config/FeatureFlags.java
+++ b/packages/apps/Launcher3/src_config/com/android/launcher3/config/FeatureFlags.java
@@ -34,7 +34,7 @@ public final class FeatureFlags {
     // Feature flag to enable moving the QSB on the 0th screen of the workspace.
     public static final boolean QSB_ON_FIRST_SCREEN = true;
     // When enabled the all-apps icon is not added to the hotseat.
-    public static final boolean NO_ALL_APPS_ICON = true;
+    public static final boolean NO_ALL_APPS_ICON = false;
     // When enabled fling down gesture on the first workspace triggers search.
     public static final boolean PULLDOWN_SEARCH = false;
     // When enabled the status bar may show dark icons based on the top of the wallpaper.


image.png

效果图如下:

image.png


在现有的UI中, 增加 ^ 键的焦点使能

diff --git a/packages/apps/Launcher3/res/layout/page_indicator.xml b/packages/apps/Launcher3/res/layout/page_indicator.xml
index 2e1b57f..22ef0b4 100644
--- a/packages/apps/Launcher3/res/layout/page_indicator.xml
+++ b/packages/apps/Launcher3/res/layout/page_indicator.xml
@@ -23,5 +23,7 @@
             android:layout_width="48dp"
             android:layout_height="match_parent"
             android:layout_gravity="center"
-            android:scaleType="centerInside"/>
+                       android:focusable="true"
+            android:scaleType="centerInside"
+                       android:background="@drawable/bg_pill_focused"/>
 </com.android.launcher3.pageindicators.PageIndicatorLineCaret>
diff --git a/packages/apps/Launcher3/src/com/android/launcher3/FocusHelper.java b/packages/apps/Launcher3/src/com/android/launcher3/FocusHelper.java
index 789c3f9..3071152 100644
--- a/packages/apps/Launcher3/src/com/android/launcher3/FocusHelper.java
+++ b/packages/apps/Launcher3/src/com/android/launcher3/FocusHelper.java
@@ -268,7 +268,6 @@ public class FocusHelper {
         // Process the focus.
         int newIconIndex = FocusLogic.handleKeyEvent(keyCode, matrix, iconIndex, pageIndex,
                 pageCount, Utilities.isRtl(v.getResources()));
-
         View newIcon = null;
         switch (newIconIndex) {
             case FocusLogic.NEXT_PAGE_FIRST_ITEM:
@@ -329,6 +328,11 @@ public class FocusHelper {
                 playSoundEffect(keyCode, v);
             }
         }
+
+               //support focus all apps.
+               if(FocusLogic.NOOP == newIconIndex){
+                       v.getRootView().findViewById(R.id.all_apps_handle).requestFocus();
+               }
         return consume;
     }


在Hotseat上, 按DPAD_DOWN, ^ 键可以获取焦点, 再输入 DPAD_CENTER, 即可打开应用列表

效果如图:

image.png


处理焦点的相关代码


定义了按键处理监听, 供 Hotseat-Icon 使用

|-- packages/apps/Launcher3/src/com/android/launcher3/FocusHelper.java


/**
 * A keyboard listener we set on all the workspace icons.
 */
class IconKeyEventListener implements View.OnKeyListener {
    @Override
    public boolean onKey(View v, int keyCode, KeyEvent event) {
        return FocusHelper.handleIconKeyEvent(v, keyCode, event);
    }
}
/**
 * A keyboard listener we set on all the hotseat buttons.
 */
class HotseatIconKeyEventListener implements View.OnKeyListener {
    @Override
    public boolean onKey(View v, int keyCode, KeyEvent event) {
        return FocusHelper.handleHotseatButtonKeyEvent(v, keyCode, event);
    }
}
/**
 * A keyboard listener we set on full screen pages (e.g. custom content).
 */
class FullscreenKeyEventListener implements View.OnKeyListener {
    @Override
    public boolean onKey(View v, int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT || keyCode == KeyEvent.KEYCODE_DPAD_RIGHT
                || keyCode == KeyEvent.KEYCODE_PAGE_DOWN || keyCode == KeyEvent.KEYCODE_PAGE_UP) {
            // Handle the key event just like a workspace icon would in these cases. In this case,
            // it will basically act as if there is a single icon in the top left (so you could
            // think of the fullscreen page as a focusable fullscreen widget).
            return FocusHelper.handleIconKeyEvent(v, keyCode, event);
        }
        return false;
    }
}
    /**
     * Handles key events in the workspace hotseat (bottom of the screen).
     * <p>Currently we don't special case for the phone UI in different orientations, even though
     * the hotseat is on the side in landscape mode. This is to ensure that accessibility
     * consistency is maintained across rotations.
     */
    static boolean handleHotseatButtonKeyEvent(View v, int keyCode, KeyEvent e) {
        boolean consume = FocusLogic.shouldConsume(keyCode);
        if (e.getAction() == KeyEvent.ACTION_UP || !consume) {
            return consume;
        }
        final Launcher launcher = Launcher.getLauncher(v.getContext());
        final DeviceProfile profile = launcher.getDeviceProfile();
        if (DEBUG) {
            Log.v(TAG, String.format(
                    "Handle HOTSEAT BUTTONS keyevent=[%s] on hotseat buttons, isVertical=%s",
                    KeyEvent.keyCodeToString(keyCode), profile.isVerticalBarLayout()));
        }
        // Initialize the variables.
        final Workspace workspace = (Workspace) v.getRootView().findViewById(R.id.workspace);
        final ShortcutAndWidgetContainer hotseatParent = (ShortcutAndWidgetContainer) v.getParent();
        final CellLayout hotseatLayout = (CellLayout) hotseatParent.getParent();
        final ItemInfo itemInfo = (ItemInfo) v.getTag();
        int pageIndex = workspace.getNextPage();
        int pageCount = workspace.getChildCount();
        int iconIndex = hotseatParent.indexOfChild(v);
        int iconRank = ((CellLayout.LayoutParams) hotseatLayout.getShortcutsAndWidgets()
                .getChildAt(iconIndex).getLayoutParams()).cellX;
        final CellLayout iconLayout = (CellLayout) workspace.getChildAt(pageIndex);
        if (iconLayout == null) {
            // This check is to guard against cases where key strokes rushes in when workspace
            // child creation/deletion is still in flux. (e.g., during drop or fling
            // animation.)
            return consume;
        }
        final ViewGroup iconParent = iconLayout.getShortcutsAndWidgets();
        ViewGroup parent = null;
        int[][] matrix = null;
        if (keyCode == KeyEvent.KEYCODE_DPAD_UP &&
                !profile.isVerticalBarLayout()) {
            matrix = FocusLogic.createSparseMatrixWithHotseat(iconLayout, hotseatLayout, profile);
            iconIndex += iconParent.getChildCount();
            parent = iconParent;
        } else if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT &&
                profile.isVerticalBarLayout()) {
            matrix = FocusLogic.createSparseMatrixWithHotseat(iconLayout, hotseatLayout, profile);
            iconIndex += iconParent.getChildCount();
            parent = iconParent;
        } else if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT &&
                profile.isVerticalBarLayout()) {
            keyCode = KeyEvent.KEYCODE_PAGE_DOWN;
        } else if (isUninstallKeyChord(e)) {
            matrix = FocusLogic.createSparseMatrix(iconLayout);
            if (UninstallDropTarget.supportsDrop(launcher, itemInfo)) {
                UninstallDropTarget.startUninstallActivity(launcher, itemInfo);
            }
        } else if (isDeleteKeyChord(e)) {
            matrix = FocusLogic.createSparseMatrix(iconLayout);
            launcher.removeItem(v, itemInfo, true /* deleteFromDb */);
        } else {
            // For other KEYCODE_DPAD_LEFT and KEYCODE_DPAD_RIGHT navigation, do not use the
            // matrix extended with hotseat.
            matrix = FocusLogic.createSparseMatrix(hotseatLayout);
            parent = hotseatParent;
        }
        // Process the focus.
        int newIconIndex = FocusLogic.handleKeyEvent(keyCode, matrix, iconIndex, pageIndex,
                pageCount, Utilities.isRtl(v.getResources()));
        View newIcon = null;
        switch (newIconIndex) {
            case FocusLogic.NEXT_PAGE_FIRST_ITEM:
                parent = getCellLayoutChildrenForIndex(workspace, pageIndex + 1);
                newIcon = parent.getChildAt(0);
                // TODO(hyunyoungs): handle cases where the child is not an icon but
                // a folder or a widget.
                workspace.snapToPage(pageIndex + 1);
                break;
            case FocusLogic.PREVIOUS_PAGE_FIRST_ITEM:
                parent = getCellLayoutChildrenForIndex(workspace, pageIndex - 1);
                newIcon = parent.getChildAt(0);
                // TODO(hyunyoungs): handle cases where the child is not an icon but
                // a folder or a widget.
                workspace.snapToPage(pageIndex - 1);
                break;
            case FocusLogic.PREVIOUS_PAGE_LAST_ITEM:
                parent = getCellLayoutChildrenForIndex(workspace, pageIndex - 1);
                newIcon = parent.getChildAt(parent.getChildCount() - 1);
                // TODO(hyunyoungs): handle cases where the child is not an icon but
                // a folder or a widget.
                workspace.snapToPage(pageIndex - 1);
                break;
            case FocusLogic.PREVIOUS_PAGE_LEFT_COLUMN:
            case FocusLogic.PREVIOUS_PAGE_RIGHT_COLUMN:
                // Go to the previous page but keep the focus on the same hotseat icon.
                workspace.snapToPage(pageIndex - 1);
                // If the page we are going to is fullscreen, have it take the focus from hotseat.
                CellLayout prevPage = (CellLayout) workspace.getPageAt(pageIndex - 1);
                boolean isPrevPageFullscreen = ((CellLayout.LayoutParams) prevPage
                        .getShortcutsAndWidgets().getChildAt(0).getLayoutParams()).isFullscreen;
                if (isPrevPageFullscreen) {
                    workspace.getPageAt(pageIndex - 1).requestFocus();
                }
                break;
            case FocusLogic.NEXT_PAGE_LEFT_COLUMN:
            case FocusLogic.NEXT_PAGE_RIGHT_COLUMN:
                // Go to the next page but keep the focus on the same hotseat icon.
                workspace.snapToPage(pageIndex + 1);
                // If the page we are going to is fullscreen, have it take the focus from hotseat.
                CellLayout nextPage = (CellLayout) workspace.getPageAt(pageIndex + 1);
                boolean isNextPageFullscreen = ((CellLayout.LayoutParams) nextPage
                        .getShortcutsAndWidgets().getChildAt(0).getLayoutParams()).isFullscreen;
                if (isNextPageFullscreen) {
                    workspace.getPageAt(pageIndex + 1).requestFocus();
                }
                break;
        }
        if (parent == iconParent && newIconIndex >= iconParent.getChildCount()) {
            newIconIndex -= iconParent.getChildCount();
        }
        if (parent != null) {
            if (newIcon == null && newIconIndex >= 0) {
                newIcon = parent.getChildAt(newIconIndex);
            }
            if (newIcon != null) {
                newIcon.requestFocus();
                playSoundEffect(keyCode, v);
            }
        }
//当HOTSEAT中的ICON获取到焦点, 并触发了按键DOWN时, newIconIndex = -1.
  //Support focuse all apps.
  if(FocusLogic.NOOP == newIconIndex){
//让 ^ 键请求焦点
//当然, 也可以直接调用显示所有应用的函数显示应用列表
    v.getRootView().findViewById(R.id.all_apps_handle).requestFocus();
  }
        return consume;
    }


|-- packages/apps/Launcher3/src/com/android/launcher3/Workspace.java


void addInScreen(View child, long container, long screenId, int x, int y, int spanX, int spanY,
            boolean insert, boolean computeXYFromRank) {
        if (container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
            if (getScreenWithId(screenId) == null) {
                Log.e(TAG, "Skipping child, screenId " + screenId + " not found");
                // DEBUGGING - Print out the stack trace to see where we are adding from
                new Throwable().printStackTrace();
                return;
            }
        }
        if (screenId == EXTRA_EMPTY_SCREEN_ID) {
            // This should never happen
            throw new RuntimeException("Screen id should not be EXTRA_EMPTY_SCREEN_ID");
        }
        final CellLayout layout;
        if (container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
            layout = mLauncher.getHotseat().getLayout();
            child.setOnKeyListener(new HotseatIconKeyEventListener());
            // Hide folder title in the hotseat
            if (child instanceof FolderIcon) {
                ((FolderIcon) child).setTextVisible(false);
            }
            if (computeXYFromRank) {
                x = mLauncher.getHotseat().getCellXFromOrder((int) screenId);
                y = mLauncher.getHotseat().getCellYFromOrder((int) screenId);
            } else {
                screenId = mLauncher.getHotseat().getOrderInHotseat(x, y);
            }
        } else {
            // Show folder title if not in the hotseat
            if (child instanceof FolderIcon) {
                ((FolderIcon) child).setTextVisible(true);
            }
            layout = getScreenWithId(screenId);
            child.setOnKeyListener(new IconKeyEventListener());
        }
   ...
   }


|-- packages/apps/Launcher3/src/com/android/launcher3/Hotseat.java


void resetLayout() {
        mContent.removeAllViewsInLayout();
        if (!FeatureFlags.NO_ALL_APPS_ICON) {
            // Add the Apps button
            Context context = getContext();
            int allAppsButtonRank = mLauncher.getDeviceProfile().inv.getAllAppsButtonRank();
            LayoutInflater inflater = LayoutInflater.from(context);
            TextView allAppsButton = (TextView)
                    inflater.inflate(R.layout.all_apps_button, mContent, false);
            Drawable d = context.getResources().getDrawable(R.drawable.all_apps_button_icon);
            mLauncher.resizeIconDrawable(d);
            int scaleDownPx = getResources().getDimensionPixelSize(R.dimen.all_apps_button_scale_down);
            Rect bounds = d.getBounds();
            d.setBounds(bounds.left, bounds.top + scaleDownPx / 2, bounds.right - scaleDownPx,
                    bounds.bottom - scaleDownPx / 2);
            allAppsButton.setCompoundDrawables(null, d, null, null);
            allAppsButton.setContentDescription(context.getString(R.string.all_apps_button_label));
            allAppsButton.setOnKeyListener(new HotseatIconKeyEventListener());
            if (mLauncher != null) {
                mLauncher.setAllAppsButton(allAppsButton);
                allAppsButton.setOnTouchListener(mLauncher.getHapticFeedbackTouchListener());
                allAppsButton.setOnClickListener(mLauncher);
                allAppsButton.setOnLongClickListener(mLauncher);
                allAppsButton.setOnFocusChangeListener(mLauncher.mFocusHandler);
            }
            // Note: We do this to ensure that the hotseat is always laid out in the orientation of
            // the hotseat in order regardless of which orientation they were added
            int x = getCellXFromOrder(allAppsButtonRank);
            int y = getCellYFromOrder(allAppsButtonRank);
            CellLayout.LayoutParams lp = new CellLayout.LayoutParams(x, y, 1, 1);
            lp.canReorder = false;
            mContent.addViewToCellLayout(allAppsButton, -1, allAppsButton.getId(), lp, true);
        }
    }


相关文章
|
6天前
|
XML Android开发 数据格式
Android Launcher3 修改行数和列数
Android Launcher3 修改行数和列数
12 0
|
2天前
|
移动开发 API Android开发
构建高效Android应用:Kotlin协程的实践指南
【5月更文挑战第11天】 在移动开发领域,性能优化和资源管理是至关重要的。特别地,对于Android开发者来说,合理利用Kotlin协程可以极大地改善应用的响应性和稳定性。本文将深入探讨Kotlin协程在Android中的实际应用,包括它们如何简化异步编程模型、提高UI线程的响应性,以及减少内存消耗。我们将通过具体案例分析,了解如何在实际项目中有效地使用协程,从而帮助开发者构建更加高效的Android应用程序。
|
4天前
|
Android开发
Android应用实例(一)之---有道辞典VZ.0
Android应用实例(一)之---有道辞典VZ.0
10 2
|
13天前
|
移动开发 Java Android开发
构建高效Android应用:Kotlin协程的实践之路
【4月更文挑战第30天】在移动开发领域,随着用户需求的不断增长和设备性能的持续提升,实现流畅且高效的用户体验已成为开发者的首要任务。针对Android平台,Kotlin协程作为一种新兴的异步编程解决方案,以其轻量级线程管理和简洁的代码逻辑受到广泛关注。本文将深入探讨Kotlin协程的概念、优势以及在实际Android应用中的运用,通过实例演示如何利用协程提升应用性能和响应能力,为开发者提供一条构建更高效Android应用的实践路径。
|
15小时前
|
移动开发 Android开发 开发者
构建高效Android应用:探究Kotlin协程的优化实践
【5月更文挑战第13天】 在移动开发领域,Android平台的流畅体验至关重要。随着Kotlin语言的普及,协程作为其核心特性之一,为异步编程提供了简洁且高效的解决方案。本文将深入探讨Kotlin协程在Android应用中的优化使用,从基本概念到实际案例分析,旨在帮助开发者构建更加响应迅速、性能卓越的应用。我们将通过对比传统线程与协程的差异,展示如何利用协程简化代码结构,并通过优化实践减少资源消耗,提升用户体验。
|
1天前
|
移动开发 监控 Android开发
构建高效Android应用:Kotlin协程的实践与优化
【5月更文挑战第12天】 在移动开发领域,性能与响应性是衡量一个应用程序优劣的关键指标。特别是在Android平台上,由于设备的多样性和系统资源的限制,开发者需要精心编写代码以确保应用流畅运行。近年来,Kotlin语言因其简洁性和功能性而广受欢迎,尤其是其协程特性,为异步编程提供了强大而轻量级的解决方案。本文将深入探讨如何在Android应用中使用Kotlin协程来提升性能,以及如何针对实际问题进行优化,确保应用的高效稳定执行。
|
4天前
|
开发工具 Android开发 Windows
Android应用] 问题2:ERROR: unknown virtual device name:
Android应用] 问题2:ERROR: unknown virtual device name:
|
4天前
|
XML JSON API
转Android上基于JSON的数据交互应用
转Android上基于JSON的数据交互应用
|
6天前
|
安全 Java Android开发
构建高效Android应用:采用Kotlin进行内存优化的策略
【5月更文挑战第8天】 在移动开发领域,性能优化一直是开发者关注的焦点。特别是对于Android应用而言,合理管理内存资源是确保应用流畅运行的关键因素之一。近年来,Kotlin作为官方推荐的开发语言,以其简洁、安全和互操作性的特点受到开发者青睐。本文将深入探讨利用Kotlin语言特性,通过具体策略对Android应用的内存使用进行优化,旨在帮助开发者提高应用性能,减少内存消耗,避免常见的内存泄漏问题。
8 0
|
6天前
|
移动开发 数据库 Android开发
构建高效Android应用:Kotlin协程的全面应用
【5月更文挑战第7天】 在移动开发领域,性能优化与流畅的用户体验是至关重要的。随着Kotlin语言的流行,其并发神器——协程,已成为提升Android应用性能的重要工具。本文将深入探讨如何在Android项目中利用Kotlin协程进行异步编程、网络请求和数据库操作,以及如何通过协程简化代码结构,增强应用的响应性和稳定性。我们的目标是为开发者提供一套实用的协程使用模式和最佳实践,以便构建更加高效的Android应用。
23 3