Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3104d634 authored by Xiaohui Chen's avatar Xiaohui Chen Committed by Android (Google) Code Review
Browse files

Merge changes from topic 'xc_remove_shelf'

* changes:
  sysui: remove shelf
  DO NOT MERGE sysui: remove shelf
parents e66515f5 06917034
Loading
Loading
Loading
Loading
+0 −31
Original line number Diff line number Diff line
<?xml version="1.0" encoding="utf-8"?>
<!--
**
** Copyright 2016, 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.
-->

<!-- Container for the app shelf. -->
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/app_shelf"
    android:orientation="horizontal"
    android:layout_height="match_parent"
    android:layout_width="0dp"
    android:layout_weight="1">
    <com.android.systemui.statusbar.phone.NavigationBarApps
        android:id="@+id/navigation_bar_apps"
        android:layout_width="wrap_content"
        android:layout_height="match_parent" />
</LinearLayout>
+0 −113
Original line number Diff line number Diff line
/*
 * Copyright (C) 2015 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.systemui.statusbar.phone;

import android.app.AppGlobals;
import android.content.pm.ActivityInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Typeface;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.os.RemoteException;
import android.util.Slog;
import android.widget.ImageView;

/**
 * Retrieves the icon for an activity and sets it as the Drawable on an ImageView. The ImageView
 * is hidden if the activity isn't recognized or if there is no icon.
 */
class GetActivityIconTask extends AsyncTask<AppButtonData, Void, Drawable> {
    private final static String TAG = "GetActivityIconTask";

    private final PackageManager mPackageManager;

    // The ImageView that will receive the icon.
    private final ImageView mImageView;

    public GetActivityIconTask(PackageManager packageManager, ImageView imageView) {
        mPackageManager = packageManager;
        mImageView = imageView;
    }

    @Override
    protected Drawable doInBackground(AppButtonData... params) {
        if (params.length != 1) {
            throw new IllegalArgumentException("Expected one parameter");
        }
        AppButtonData buttonData = params[0];
        AppInfo appInfo = buttonData.appInfo;
        try {
            IPackageManager mPM = AppGlobals.getPackageManager();
            ActivityInfo ai = mPM.getActivityInfo(
                    appInfo.getComponentName(),
                    0,
                    appInfo.getUser().getIdentifier());

            if (ai == null) {
                Slog.w(TAG, "Icon not found for " + appInfo);
                return null;
            }

            Drawable unbadgedIcon = ai.loadIcon(mPackageManager);
            Drawable badgedIcon =
                    mPackageManager.getUserBadgedIcon(unbadgedIcon, appInfo.getUser());

            if (NavigationBarApps.DEBUG) {
                // Draw pinned indicator and number of running tasks.
                Bitmap bitmap = Bitmap.createBitmap(
                        badgedIcon.getIntrinsicWidth(),
                        badgedIcon.getIntrinsicHeight(),
                        Bitmap.Config.ARGB_8888);
                Canvas canvas = new Canvas(bitmap);
                badgedIcon.setBounds(
                        0, 0, badgedIcon.getIntrinsicWidth(), badgedIcon.getIntrinsicHeight());
                badgedIcon.draw(canvas);
                Paint paint = new Paint();
                paint.setStyle(Paint.Style.FILL);
                if (buttonData.pinned) {
                    paint.setColor(Color.WHITE);
                    canvas.drawCircle(10, 10, 10, paint);
                }
                if (buttonData.tasks != null && buttonData.tasks.size() > 0) {
                    paint.setColor(Color.BLACK);
                    canvas.drawCircle(60, 30, 30, paint);
                    paint.setColor(Color.WHITE);
                    paint.setTextSize(50);
                    paint.setTypeface(Typeface.create("sans-serif", Typeface.BOLD));
                    canvas.drawText(Integer.toString(buttonData.tasks.size()), 50, 50, paint);
                }
                badgedIcon = new BitmapDrawable(null, bitmap);
            }

            return  badgedIcon;
        } catch (RemoteException e) {
            Slog.w(TAG, "Icon not found for " + appInfo, e);
            return null;
        }
    }

    @Override
    protected void onPostExecute(Drawable icon) {
        mImageView.setImageDrawable(icon);
    }
}
+0 −1123

File deleted.

Preview size limit exceeded, changes collapsed.

+0 −364
Original line number Diff line number Diff line
/*
 * Copyright (C) 2015 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.systemui.statusbar.phone;

import android.app.AppGlobals;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.ActivityInfo;
import android.content.pm.IPackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.UserInfo;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.Slog;

import com.android.internal.annotations.VisibleForTesting;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * Data model and controller for app icons appearing in the navigation bar. The data is stored on
 * disk in SharedPreferences. Each icon has a separate pref entry consisting of a flattened
 * ComponentName.
 */
class NavigationBarAppsModel {
    public interface OnAppsChangedListener {
        void onPinnedAppsChanged();
    }

    public class ResolvedApp {
        Intent launchIntent;
        ResolveInfo ri;
    }

    private final static String TAG = "NavigationBarAppsModel";

    // Default number of apps to load initially.
    private final static int NUM_INITIAL_APPS = 4;

    // Preferences file name.
    private final static String SHARED_PREFERENCES_NAME = "com.android.systemui.navbarapps";

    // Preference name for the version of the other preferences.
    private final static String VERSION_PREF = "version";

    // Current version number for preferences.
    private final static int CURRENT_VERSION = 3;

    // Preference name for the number of app icons.
    private final static String APP_COUNT_PREF = "app_count";

    // Preference name prefix for each app's info. The actual pref has an integer appended to it.
    private final static String APP_PREF_PREFIX = "app_";

    // User serial number prefix for each app's info. The actual pref has an integer appended to it.
    private final static String APP_USER_PREFIX = "app_user_";

    // Character separating current user serial number from the user-specific part of a pref.
    // Example "22|app_user_2" - when logged as user with serial 22, we'll use this pref for the
    // user serial of the third app of the logged-in user.
    private final static char USER_SEPARATOR = '|';

    private final Context mContext;
    private final UserManager mUserManager;
    private final SharedPreferences mPrefs;

    // Apps are represented as an ordered list of app infos.
    private List<AppInfo> mApps = new ArrayList<AppInfo>();

    private List<OnAppsChangedListener> mOnAppsChangedListeners =
            new ArrayList<OnAppsChangedListener>();

    // Id of the current user.
    private int mCurrentUserId = -1;

    // Serial number of the current user.
    private long mCurrentUserSerialNumber = -1;

    public NavigationBarAppsModel(Context context) {
        mContext = context;
        mPrefs = mContext.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE);
        mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);

        int version = mPrefs.getInt(VERSION_PREF, -1);
        if (version != CURRENT_VERSION) {
            // Since the data format changed, clean everything.
            SharedPreferences.Editor edit = mPrefs.edit();
            edit.clear();
            edit.putInt(VERSION_PREF, CURRENT_VERSION);
            edit.apply();
        }
    }

    @VisibleForTesting
    protected IPackageManager getPackageManager() {
        return AppGlobals.getPackageManager();
    }

    // Returns a resolved app info for a given app info, or null if the app info is unlauncheable.
    public ResolvedApp resolveApp(AppInfo appInfo) {
        ComponentName component = appInfo.getComponentName();
        int appUserId = appInfo.getUser().getIdentifier();

        if (mCurrentUserId != appUserId) {
            // Check if app user is a profile of current user and the app user is enabled.
            UserInfo appUserInfo = mUserManager.getUserInfo(appUserId);
            UserInfo currentUserInfo = mUserManager.getUserInfo(mCurrentUserId);
            if (appUserInfo == null || currentUserInfo == null
                    || appUserInfo.profileGroupId == UserInfo.NO_PROFILE_GROUP_ID
                    || appUserInfo.profileGroupId != currentUserInfo.profileGroupId
                    || !appUserInfo.isEnabled()) {
                Slog.e(TAG, "User " + appUserId +
                        " is is not a profile of the current user, or is disabled.");
                return null;
            }
        }

        // This code is based on LauncherAppsService.startActivityAsUser code.
        Intent launchIntent = new Intent(Intent.ACTION_MAIN);
        launchIntent.addCategory(Intent.CATEGORY_LAUNCHER);
        launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        launchIntent.setPackage(component.getPackageName());

        try {
            ActivityInfo info = getPackageManager().getActivityInfo(component, 0, appUserId);
            if (info == null) {
                Slog.e(TAG, "Activity " + component + " is not installed.");
                return null;
            }

            if (!info.exported) {
                Slog.e(TAG, "Activity " + component + " doesn't have 'exported' attribute.");
                return null;
            }
        } catch (RemoteException e) {
            Slog.e(TAG, "Failed to get activity info for " + component, e);
            return null;
        }

        // Check that the component actually has Intent.CATEGORY_LAUNCHER
        // as calling startActivityAsUser ignores the category and just
        // resolves based on the component if present.
        List<ResolveInfo> apps = mContext.getPackageManager().queryIntentActivitiesAsUser(launchIntent,
                0 /* flags */, appUserId);
        final int size = apps.size();
        for (int i = 0; i < size; ++i) {
            ResolveInfo ri = apps.get(i);
            ActivityInfo activityInfo = ri.activityInfo;
            if (activityInfo.packageName.equals(component.getPackageName()) &&
                    activityInfo.name.equals(component.getClassName())) {
                // Found an activity with category launcher that matches
                // this component so ok to launch.
                launchIntent.setComponent(component);
                ResolvedApp resolvedApp = new ResolvedApp();
                resolvedApp.launchIntent = launchIntent;
                resolvedApp.ri = ri;
                return resolvedApp;
            }
        }

        Slog.i(TAG, "Activity doesn't have category Intent.CATEGORY_LAUNCHER " + component);
        return null;
    }

    public void addOnAppsChangedListener(OnAppsChangedListener listener) {
        mOnAppsChangedListeners.add(listener);
    }

    public void removeOnAppsChangedListener(OnAppsChangedListener listener) {
        mOnAppsChangedListeners.remove(listener);
    }

    /**
     * Reinitializes the model for a new user.
     */
    public void setCurrentUser(int userId) {
        mCurrentUserId = userId;
        mCurrentUserSerialNumber = mUserManager.getSerialNumberForUser(new UserHandle(userId));

        mApps.clear();

        int appCount = mPrefs.getInt(userPrefixed(APP_COUNT_PREF), -1);
        if (appCount >= 0) {
            loadAppsFromPrefs(appCount);
        } else {
            // We switched to this user for the first time ever. This is a good opportunity to clean
            // prefs for users deleted in the past.
            removePrefsForDeletedUsers();

            addDefaultApps();
        }
    }

    /**
     * Removes prefs for users that don't exist on the device.
     */
    private void removePrefsForDeletedUsers() {
        // Build a set of string representations of serial numbers of the device users.
        final List<UserInfo> users = mUserManager.getUsers();
        final int userCount = users.size();

        final Set<String> userSerials = new HashSet<String> ();

        for (int i = 0; i < userCount; ++i) {
            userSerials.add(Long.toString(users.get(i).serialNumber));
        }

        // Walk though all prefs and delete ones which user is not in the string set.
        final Map<String, ?> allPrefs = mPrefs.getAll();
        final SharedPreferences.Editor edit = mPrefs.edit();

        for (Map.Entry<String, ?> pref : allPrefs.entrySet()) {
            final String key = pref.getKey();
            if (key.equals(VERSION_PREF)) continue;

            final int userSeparatorPos = key.indexOf(USER_SEPARATOR);

            if (userSeparatorPos < 0) {
                // Removing anomalous pref with no user.
                edit.remove(key);
                continue;
            }

            final String prefUserSerial = key.substring(0, userSeparatorPos);

            if (!userSerials.contains(prefUserSerial)) {
                // Removes pref for a not existing user.
                edit.remove(key);
                continue;
            }
        }

        edit.apply();
    }

    /** Returns the list of apps. */
    public List<AppInfo> getApps() {
        return mApps;
    }

    /** Sets the list of apps and saves it. */
    public void setApps(List<AppInfo> apps) {
        mApps = apps;
        savePrefs();

        int size = mOnAppsChangedListeners.size();
        for (int i = 0; i < size; ++i) {
            mOnAppsChangedListeners.get(i).onPinnedAppsChanged();
        }
    }

    /** Saves the current model to disk. */
    private void savePrefs() {
        SharedPreferences.Editor edit = mPrefs.edit();
        int appCount = mApps.size();
        edit.putInt(userPrefixed(APP_COUNT_PREF), appCount);
        for (int i = 0; i < appCount; i++) {
            final AppInfo appInfo = mApps.get(i);
            String componentNameString = appInfo.getComponentName().flattenToString();
            edit.putString(prefNameForApp(i), componentNameString);
            long userSerialNumber = mUserManager.getSerialNumberForUser(appInfo.getUser());
            edit.putLong(prefUserForApp(i), userSerialNumber);
        }
        // Start an asynchronous disk write.
        edit.apply();
    }

    /** Loads AppInfo from prefs. Returns null if something is wrong. */
    private AppInfo loadAppFromPrefs(int index) {
        String prefValue = mPrefs.getString(prefNameForApp(index), null);
        if (prefValue == null) {
            Slog.w(TAG, "Couldn't find pref " + prefNameForApp(index));
            return null;
        }
        ComponentName componentName = ComponentName.unflattenFromString(prefValue);
        if (componentName == null) {
            Slog.w(TAG, "Invalid component name " + prefValue);
            return null;
        }
        long userSerialNumber = mPrefs.getLong(prefUserForApp(index), -1);
        if (userSerialNumber == -1) {
            Slog.w(TAG, "Couldn't find pref " + prefUserForApp(index));
            return null;
        }
        UserHandle appUser = mUserManager.getUserForSerialNumber(userSerialNumber);
        if (appUser == null) {
            Slog.w(TAG, "No user for serial " + userSerialNumber);
            return null;
        }
        AppInfo appInfo = new AppInfo(componentName, appUser);
        if (resolveApp(appInfo) == null) {
            return null;
        }
        return appInfo;
    }

    /** Loads the list of apps from SharedPreferences. */
    private void loadAppsFromPrefs(int appCount) {
        for (int i = 0; i < appCount; i++) {
            AppInfo appInfo = loadAppFromPrefs(i);
            if (appInfo != null) {
                mApps.add(appInfo);
            }
        }

        if (appCount != mApps.size()) savePrefs();
    }

    /** Adds the first few apps from the owner profile. Used for demo purposes. */
    private void addDefaultApps() {
        // Get a list of all app activities.
        final Intent queryIntent = new Intent(Intent.ACTION_MAIN, null);
        queryIntent.addCategory(Intent.CATEGORY_LAUNCHER);

        final List<ResolveInfo> apps = mContext.getPackageManager().queryIntentActivitiesAsUser(
                queryIntent, 0 /* flags */, mCurrentUserId);
        final int appCount = apps.size();
        for (int i = 0; i < NUM_INITIAL_APPS && i < appCount; i++) {
            ResolveInfo ri = apps.get(i);
            ComponentName componentName = new ComponentName(
                    ri.activityInfo.packageName, ri.activityInfo.name);
            mApps.add(new AppInfo(componentName, new UserHandle(mCurrentUserId)));
        }

        savePrefs();
    }

    /** Returns a pref prefixed with the serial number of the current user. */
    private String userPrefixed(String pref) {
        return Long.toString(mCurrentUserSerialNumber) + USER_SEPARATOR + pref;
    }

    /** Returns the pref name for the app at a given index. */
    private String prefNameForApp(int index) {
        return userPrefixed(APP_PREF_PREFIX + Integer.toString(index));
    }

    /** Returns the pref name for the app's user at a given index. */
    private String prefUserForApp(int index) {
        return userPrefixed(APP_USER_PREFIX + Integer.toString(index));
    }

}
+7 −23
Original line number Diff line number Diff line
@@ -43,8 +43,6 @@ public class NavigationBarInflaterView extends FrameLayout implements TunerServi
    private static final String RECENT = "recent";
    private static final String NAVSPACE = "space";

    private static final String APP_SHELF = "app_shelf";

    public static final String GRAVITY_SEPARATOR = ";";
    public static final String BUTTON_SEPARATOR = ",";

@@ -141,19 +139,13 @@ public class NavigationBarInflaterView extends FrameLayout implements TunerServi
        inflateButtons(start, (ViewGroup) mRot90.findViewById(R.id.ends_group),
                (ViewGroup) mRot90.findViewById(R.id.ends_group_lightsout), true);

        if (center.length == 1 && APP_SHELF.equals(center[0])) {
            inflateShelf((LinearLayout) mRot0.findViewById(R.id.ends_group),
                    (LinearLayout) mRot0.findViewById(R.id.ends_group_lightsout), false);
            inflateShelf((LinearLayout) mRot90.findViewById(R.id.ends_group),
                    (LinearLayout) mRot90.findViewById(R.id.ends_group_lightsout), true);
        } else {
        inflateButtons(center, (ViewGroup) mRot0.findViewById(R.id.center_group),
                (ViewGroup) mRot0.findViewById(R.id.center_group_lightsout), false);
        inflateButtons(center, (ViewGroup) mRot90.findViewById(R.id.center_group),
                (ViewGroup) mRot90.findViewById(R.id.center_group_lightsout), true);

        addGravitySpacer((LinearLayout) mRot0.findViewById(R.id.ends_group));
        addGravitySpacer((LinearLayout) mRot90.findViewById(R.id.ends_group));
        }

        inflateButtons(end, (ViewGroup) mRot0.findViewById(R.id.ends_group),
                (ViewGroup) mRot0.findViewById(R.id.ends_group_lightsout), false);
@@ -161,14 +153,6 @@ public class NavigationBarInflaterView extends FrameLayout implements TunerServi
                (ViewGroup) mRot90.findViewById(R.id.ends_group_lightsout), true);
    }

    private void inflateShelf(LinearLayout layout, LinearLayout lightsOut, boolean landscape) {
        View v = (landscape ? mLandscapeInflater : mLayoutInflater)
                .inflate(R.layout.apps_bar, layout, false);
        layout.addView(v);
        addToDispatchers(v);
        copyToLightsout(v, lightsOut);
    }

    private void addGravitySpacer(LinearLayout layout) {
        layout.addView(new Space(mContext), new LinearLayout.LayoutParams(0, 0, 1));
    }
Loading