1/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.launcher3.compat;
18
19import android.content.Context;
20import android.content.pm.PackageInstaller;
21import android.content.pm.PackageInstaller.SessionCallback;
22import android.content.pm.PackageInstaller.SessionInfo;
23import android.util.Log;
24import android.util.SparseArray;
25
26import com.android.launcher3.IconCache;
27import com.android.launcher3.LauncherAppState;
28
29import java.util.ArrayList;
30import java.util.HashSet;
31
32public class PackageInstallerCompatVL extends PackageInstallerCompat {
33
34    private static final String TAG = "PackageInstallerCompatVL";
35    private static final boolean DEBUG = false;
36
37    private final SparseArray<SessionInfo> mPendingReplays = new SparseArray<SessionInfo>();
38    private final HashSet<String> mPendingBadgeUpdates = new HashSet<String>();
39    private final PackageInstaller mInstaller;
40    private final IconCache mCache;
41
42    private boolean mResumed;
43    private boolean mBound;
44
45    PackageInstallerCompatVL(Context context) {
46        mInstaller = context.getPackageManager().getPackageInstaller();
47        LauncherAppState.setApplicationContext(context.getApplicationContext());
48        mCache = LauncherAppState.getInstance().getIconCache();
49
50        mResumed = false;
51        mBound = false;
52
53        mInstaller.registerSessionCallback(mCallback);
54
55        // On start, send updates for all active sessions
56        for (SessionInfo info : mInstaller.getAllSessions()) {
57            mPendingReplays.append(info.getSessionId(), info);
58        }
59    }
60
61    @Override
62    public HashSet<String> updateAndGetActiveSessionCache() {
63        HashSet<String> activePackages = new HashSet<String>();
64        UserHandleCompat user = UserHandleCompat.myUserHandle();
65        for (SessionInfo info : mInstaller.getAllSessions()) {
66            addSessionInfoToCahce(info, user);
67            if (info.getAppPackageName() != null) {
68                activePackages.add(info.getAppPackageName());
69            }
70        }
71        return activePackages;
72    }
73
74    private void addSessionInfoToCahce(SessionInfo info, UserHandleCompat user) {
75        String packageName = info.getAppPackageName();
76        if (packageName != null) {
77            mCache.cachePackageInstallInfo(packageName, user, info.getAppIcon(),
78                    info.getAppLabel());
79        }
80    }
81
82    @Override
83    public void onStop() {
84    }
85
86    @Override
87    public void onFinishBind() {
88        mBound = true;
89        replayUpdates(null);
90    }
91
92    @Override
93    public void onPause() {
94        mResumed = false;
95    }
96
97    @Override
98    public void onResume() {
99        mResumed = true;
100        replayUpdates(null);
101    }
102
103    @Override
104    public void recordPackageUpdate(String packageName, int state, int progress) {
105        // No op
106    }
107
108    private void replayUpdates(PackageInstallInfo newInfo) {
109        if (DEBUG) Log.d(TAG, "updates resumed");
110        if (!mResumed || !mBound) {
111            // Not yet ready
112            return;
113        }
114        if ((mPendingReplays.size() == 0) && (newInfo == null) && mPendingBadgeUpdates.isEmpty()) {
115            // Nothing to update
116            return;
117        }
118
119        LauncherAppState app = LauncherAppState.getInstanceNoCreate();
120        if (app == null) {
121            // Try again later
122            if (DEBUG) Log.d(TAG, "app is null, delaying send");
123            return;
124        }
125
126        ArrayList<PackageInstallInfo> updates = new ArrayList<PackageInstallInfo>();
127        if ((newInfo != null) && (newInfo.state != STATUS_INSTALLED)) {
128            updates.add(newInfo);
129        }
130        for (int i = mPendingReplays.size() - 1; i >= 0; i--) {
131            SessionInfo session = mPendingReplays.valueAt(i);
132            if (session.getAppPackageName() != null) {
133                updates.add(new PackageInstallInfo(session.getAppPackageName(),
134                        STATUS_INSTALLING,
135                        (int) (session.getProgress() * 100)));
136            }
137        }
138        mPendingReplays.clear();
139        if (!updates.isEmpty()) {
140            app.setPackageState(updates);
141        }
142
143        if (!mPendingBadgeUpdates.isEmpty()) {
144            for (String pkg : mPendingBadgeUpdates) {
145                app.updatePackageBadge(pkg);
146            }
147            mPendingBadgeUpdates.clear();
148        }
149    }
150
151    private final SessionCallback mCallback = new SessionCallback() {
152
153        @Override
154        public void onCreated(int sessionId) {
155            pushSessionBadgeToLauncher(sessionId);
156        }
157
158        @Override
159        public void onFinished(int sessionId, boolean success) {
160            mPendingReplays.remove(sessionId);
161            SessionInfo session = mInstaller.getSessionInfo(sessionId);
162            if ((session != null) && (session.getAppPackageName() != null)) {
163                mPendingBadgeUpdates.remove(session.getAppPackageName());
164                // Replay all updates with a one time update for this installed package. No
165                // need to store this record for future updates, as the app list will get
166                // refreshed on resume.
167                replayUpdates(new PackageInstallInfo(session.getAppPackageName(),
168                        success ? STATUS_INSTALLED : STATUS_FAILED, 0));
169            }
170        }
171
172        @Override
173        public void onProgressChanged(int sessionId, float progress) {
174            SessionInfo session = mInstaller.getSessionInfo(sessionId);
175            if (session != null) {
176                mPendingReplays.put(sessionId, session);
177                replayUpdates(null);
178            }
179        }
180
181        @Override
182        public void onActiveChanged(int sessionId, boolean active) { }
183
184        @Override
185        public void onBadgingChanged(int sessionId) {
186            pushSessionBadgeToLauncher(sessionId);
187        }
188
189        private void pushSessionBadgeToLauncher(int sessionId) {
190            SessionInfo session = mInstaller.getSessionInfo(sessionId);
191            if (session != null) {
192                addSessionInfoToCahce(session, UserHandleCompat.myUserHandle());
193                if (session.getAppPackageName() != null) {
194                    mPendingBadgeUpdates.add(session.getAppPackageName());
195                }
196                mPendingReplays.put(sessionId, session);
197                replayUpdates(null);
198            }
199        }
200    };
201}
202