1 /*
2  * Copyright (C) 2012 The Android Open Source Project
3  *
4  * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php
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 
17 package com.android.ide.eclipse.adt.internal.project;
18 
19 import static com.android.SdkConstants.FQCN_GRID_LAYOUT;
20 import static com.android.SdkConstants.FQCN_GRID_LAYOUT_V7;
21 import static com.android.SdkConstants.FQCN_SPACE;
22 import static com.android.SdkConstants.FQCN_SPACE_V7;
23 
24 import com.android.annotations.NonNull;
25 import com.android.annotations.Nullable;
26 import com.android.ide.eclipse.adt.AdtUtils;
27 import com.android.ide.eclipse.adt.internal.actions.AddSupportJarAction;
28 import com.android.ide.eclipse.adt.internal.editors.manifest.ManifestInfo;
29 import com.android.ide.eclipse.adt.internal.sdk.ProjectState;
30 import com.android.ide.eclipse.adt.internal.sdk.ProjectState.LibraryState;
31 import com.android.ide.eclipse.adt.internal.sdk.Sdk;
32 
33 import org.eclipse.core.resources.IProject;
34 import org.eclipse.jdt.core.IJavaProject;
35 import org.eclipse.jface.dialogs.MessageDialog;
36 import org.eclipse.swt.widgets.Display;
37 
38 /**
39  * Helper class for the Android Support Library. The support library provides
40  * (for example) a backport of GridLayout, which must be used as a library
41  * project rather than a jar library since it has resources. This class provides
42  * support for finding the library project, or downloading and installing it on
43  * demand if it does not, as well as translating tags such as
44  * {@code <GridLayout>} into {@code <com.android.support.v7.GridLayout>} if it
45  * does not.
46  */
47 public class SupportLibraryHelper {
48     /**
49      * Returns the correct tag to use for the given view tag. This is normally
50      * the same as the tag itself. However, for some views which are not available
51      * on all platforms, this will:
52      * <ul>
53      *  <li> Check if the view is available in the compatibility library,
54      *       and if so, if the support library is not installed, will offer to
55      *       install it via the SDK manager.
56      *  <li> (The tool may also offer to adjust the minimum SDK of the project
57      *       up to a level such that the given tag is supported directly, and then
58      *       this method will return the original tag.)
59      *  <li> Check whether the compatibility library is included in the project, and
60      *       if not, offer to copy it into the workspace and add a library dependency.
61      *  <li> Return the alternative tag. For example, for "GridLayout", it will
62      *       (if the minimum SDK is less than 14) return "com.android.support.v7.GridLayout"
63      *       instead.
64      * </ul>
65      *
66      * @param project the project to add the dependency into
67      * @param tag the tag to look up, such as "GridLayout"
68      * @return the tag to use in the layout, normally the same as the input tag but possibly
69      *      an equivalent compatibility library tag instead.
70      */
71     @NonNull
getTagFor(@onNull IProject project, @NonNull String tag)72     public static String getTagFor(@NonNull IProject project, @NonNull String tag) {
73         boolean isGridLayout = tag.equals(FQCN_GRID_LAYOUT);
74         boolean isSpace = tag.equals(FQCN_SPACE);
75         if (isGridLayout || isSpace) {
76             int minSdk = ManifestInfo.get(project).getMinSdkVersion();
77             if (minSdk < 14) {
78                 // See if the support library is installed in the SDK area
79                 // See if there is a local project in the workspace providing the
80                 // project
81                 IProject supportProject = getSupportProjectV7();
82                 if (supportProject != null) {
83                     // Make sure I have a dependency on it
84                     ProjectState state = Sdk.getProjectState(project);
85                     if (state != null) {
86                         for (LibraryState library : state.getLibraries()) {
87                             if (supportProject.equals(library.getProjectState().getProject())) {
88                                 // Found it: you have the compatibility library and have linked
89                                 // to it: use the alternative tag
90                                 return isGridLayout ? FQCN_GRID_LAYOUT_V7 : FQCN_SPACE_V7;
91                             }
92                         }
93                     }
94                 }
95 
96                 // Ask user to install it
97                 String message = String.format(
98                         "%1$s requires API level 14 or higher, or a compatibility "
99                                 + "library for older versions.\n\n"
100                                 + " Do you want to install the compatibility library?", tag);
101                 MessageDialog dialog =
102                         new MessageDialog(
103                                 Display.getCurrent().getActiveShell(),
104                                 "Warning",
105                                 null,
106                                 message,
107                                 MessageDialog.QUESTION,
108                                 new String[] {
109                                         "Install", "Cancel"
110                                 },
111                                 1 /* default button: Cancel */);
112                 int answer = dialog.open();
113                 if (answer == 0) {
114                     if (supportProject != null) {
115                         // Just add library dependency
116                         if (!AddSupportJarAction.addLibraryDependency(
117                                 supportProject,
118                                 project,
119                                 true /* waitForFinish */)) {
120                             return tag;
121                         }
122                     } else {
123                         // Install library AND add dependency
124                         if (!AddSupportJarAction.installGridLayoutLibrary(
125                                 project,
126                                 true /* waitForFinish */)) {
127                             return tag;
128                         }
129                     }
130 
131                     return isGridLayout ? FQCN_GRID_LAYOUT_V7 : FQCN_SPACE_V7;
132                 }
133             }
134         }
135 
136         return tag;
137     }
138 
139     /** Cache for {@link #getSupportProjectV7()} */
140     private static IProject sCachedProject;
141 
142     /**
143      * Finds and returns the support project in the workspace, if any.
144      *
145      * @return the android support library project, or null if not found
146      */
147     @Nullable
getSupportProjectV7()148     public static IProject getSupportProjectV7() {
149         if (sCachedProject != null) {
150             if (sCachedProject.isAccessible()) {
151                 return sCachedProject;
152             } else {
153                 sCachedProject = null;
154             }
155         }
156 
157         sCachedProject = findSupportProjectV7();
158         return sCachedProject;
159     }
160 
161     @Nullable
findSupportProjectV7()162     private static IProject findSupportProjectV7() {
163         for (IJavaProject javaProject : AdtUtils.getOpenAndroidProjects()) {
164             IProject project = javaProject.getProject();
165             ProjectState state = Sdk.getProjectState(project);
166             if (state != null && state.isLibrary()) {
167                 ManifestInfo manifestInfo = ManifestInfo.get(project);
168                 if (manifestInfo.getPackage().equals("android.support.v7.gridlayout")) { //$NON-NLS-1$
169                     return project;
170                 }
171             }
172         }
173 
174         return null;
175     }
176 }
177