blob: d26e8d64dc824c3c57efbfc7df285f4c4bd918a4 [file] [log] [blame]
Jason Monk5db8a412015-10-21 15:16:23 -07001/*
2 * Copyright (C) 2015 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.systemui.qs.tiles;
18
Jason Monkbbadff82015-11-06 15:47:26 -050019import android.app.ActivityManager;
20import android.app.Service;
Jason Monk5db8a412015-10-21 15:16:23 -070021import android.content.ComponentName;
Jason Monkbbadff82015-11-06 15:47:26 -050022import android.content.Intent;
23import android.content.ServiceConnection;
Jason Monk5db8a412015-10-21 15:16:23 -070024import android.content.pm.PackageManager;
25import android.content.pm.ServiceInfo;
Jason Monk068cb8b2015-12-02 11:30:36 -050026import android.graphics.drawable.Drawable;
Jason Monkbbadff82015-11-06 15:47:26 -050027import android.os.IBinder;
28import android.os.UserHandle;
29import android.service.quicksettings.IQSTileService;
30import android.service.quicksettings.Tile;
31import android.util.Log;
Jason Monk5db8a412015-10-21 15:16:23 -070032
33import com.android.internal.logging.MetricsLogger;
34import com.android.systemui.qs.QSTile;
Jason Monkbbadff82015-11-06 15:47:26 -050035import com.android.systemui.qs.QSTileServiceWrapper;
36import com.android.systemui.statusbar.phone.QSTileHost;
Jason Monk5db8a412015-10-21 15:16:23 -070037
38public class CustomTile extends QSTile<QSTile.State> {
39 public static final String PREFIX = "custom(";
40
Jason Monkbbadff82015-11-06 15:47:26 -050041 // We don't want to thrash binding and unbinding if the user opens and closes the panel a lot.
42 // So instead we have a period of waiting.
43 private static final long UNBIND_DELAY = 30000;
Jason Monk5db8a412015-10-21 15:16:23 -070044
Jason Monkbbadff82015-11-06 15:47:26 -050045 private final ComponentName mComponent;
46 private final Tile mTile;
47
48 private QSTileServiceWrapper mService;
49 private boolean mListening;
50 private boolean mBound;
51
52 private CustomTile(QSTileHost host, String action) {
Jason Monk5db8a412015-10-21 15:16:23 -070053 super(host);
54 mComponent = ComponentName.unflattenFromString(action);
Jason Monkbbadff82015-11-06 15:47:26 -050055 mTile = new Tile(mComponent, host);
56 try {
57 PackageManager pm = mContext.getPackageManager();
58 ServiceInfo info = pm.getServiceInfo(mComponent, 0);
59 mTile.setIcon(android.graphics.drawable.Icon
60 .createWithResource(mComponent.getPackageName(), info.icon));
61 mTile.setLabel(info.loadLabel(pm));
62 } catch (Exception e) {
63 }
Jason Monk5db8a412015-10-21 15:16:23 -070064 }
65
Jason Monkbbadff82015-11-06 15:47:26 -050066 public ComponentName getComponent() {
67 return mComponent;
68 }
69
70 public Tile getQsTile() {
71 return mTile;
72 }
73
74 public void updateState(Tile tile) {
75 Log.d("TileService", "Setting state " + tile.getLabel());
76 mTile.setIcon(tile.getIcon());
77 mTile.setLabel(tile.getLabel());
78 mTile.setContentDescription(tile.getContentDescription());
Jason Monk5db8a412015-10-21 15:16:23 -070079 }
80
81 @Override
82 public void setListening(boolean listening) {
Jason Monkbbadff82015-11-06 15:47:26 -050083 if (mListening == listening) return;
84 mListening = listening;
85 if (listening) {
86 mHandler.removeCallbacks(mUnbind);
87 if (!mBound) {
88 // TODO: Guarantee re-bind on user-switch.
89 mContext.bindServiceAsUser(new Intent().setComponent(mComponent),
90 mServiceConnection, Service.BIND_AUTO_CREATE,
91 new UserHandle(ActivityManager.getCurrentUser()));
92 mBound = true;
93 }
94 } else {
95 if (mService!= null) {
96 mService.onStopListening();
97 }
98 mHandler.postDelayed(mUnbind, UNBIND_DELAY);
99 }
100 }
101
102 @Override
103 protected void handleDestroy() {
104 super.handleDestroy();
105 mHandler.removeCallbacks(mUnbind);
106 mUnbind.run();
Jason Monk5db8a412015-10-21 15:16:23 -0700107 }
108
109 @Override
110 protected State newTileState() {
111 return new State();
112 }
113
114 @Override
115 protected void handleUserSwitch(int newUserId) {
116 super.handleUserSwitch(newUserId);
117 }
118
119 @Override
120 protected void handleClick() {
Jason Monkbbadff82015-11-06 15:47:26 -0500121 if (mService != null) {
122 mService.onClick();
123 } else {
124 Log.e(TAG, "Click with no service " + getTileSpec());
125 }
Jason Monk5db8a412015-10-21 15:16:23 -0700126 MetricsLogger.action(mContext, getMetricsCategory(), mComponent.getPackageName());
127 }
128
129 @Override
130 protected void handleLongClick() {
131 }
132
133 @Override
134 protected void handleUpdateState(State state, Object arg) {
Jason Monkbbadff82015-11-06 15:47:26 -0500135 state.visible = true;
Jason Monk068cb8b2015-12-02 11:30:36 -0500136 Drawable drawable = mTile.getIcon().loadDrawable(mContext);
137 drawable.setTint(mContext.getColor(android.R.color.white));
138 state.icon = new DrawableIcon(drawable);
Jason Monkbbadff82015-11-06 15:47:26 -0500139 state.label = mTile.getLabel();
140 if (mTile.getContentDescription() != null) {
141 state.contentDescription = mTile.getContentDescription();
142 } else {
Jason Monk5db8a412015-10-21 15:16:23 -0700143 state.contentDescription = state.label;
Jason Monk5db8a412015-10-21 15:16:23 -0700144 }
145 }
146
147 @Override
148 public int getMetricsCategory() {
149 return MetricsLogger.QS_INTENT;
150 }
Jason Monkbbadff82015-11-06 15:47:26 -0500151
152 private final ServiceConnection mServiceConnection = new ServiceConnection() {
153 @Override
154 public void onServiceConnected(ComponentName name, IBinder service) {
155 mService = new QSTileServiceWrapper(IQSTileService.Stub.asInterface(service));
156 if (mListening) {
157 mService.setQSTile(mTile);
158 mService.onStartListening();
159 } else {
160 mService.onStopListening();
161 }
162 }
163
164 @Override
165 public void onServiceDisconnected(ComponentName name) {
166 }
167 };
168
169 private final Runnable mUnbind = new Runnable() {
170 @Override
171 public void run() {
172 mContext.unbindService(mServiceConnection);
173 mBound = false;
174 }
175 };
176
177 public static ComponentName getComponentFromSpec(String spec) {
178 final String action = spec.substring(PREFIX.length(), spec.length() - 1);
179 if (action.isEmpty()) {
180 throw new IllegalArgumentException("Empty custom tile spec action");
181 }
182 return ComponentName.unflattenFromString(action);
183 }
184
185 public static QSTile<?> create(QSTileHost host, String spec) {
186 if (spec == null || !spec.startsWith(PREFIX) || !spec.endsWith(")")) {
187 throw new IllegalArgumentException("Bad custom tile spec: " + spec);
188 }
189 final String action = spec.substring(PREFIX.length(), spec.length() - 1);
190 if (action.isEmpty()) {
191 throw new IllegalArgumentException("Empty custom tile spec action");
192 }
193 return new CustomTile(host, action);
194 }
Jason Monk5db8a412015-10-21 15:16:23 -0700195}