Revision | 3b98784b5fa50568016a79eecdc049064239c6ab (tree) |
---|---|
Time | 2017-08-23 03:42:44 |
Author | HMML <hmml3939@gmai...> |
Commiter | HMML |
Theme downloader impl, first worked version. (WIP on UI!)
@@ -29,10 +29,10 @@ dependencies { | ||
29 | 29 | exclude group: 'com.android.support', module: 'support-annotations' |
30 | 30 | }) |
31 | 31 | compile 'com.android.support:appcompat-v7:25.1.0' |
32 | - compile 'com.google.android.gms:play-services-location:8.+' | |
32 | + compile 'com.google.android.gms:play-services-location:8.4.0' | |
33 | 33 | compile 'com.koushikdutta.ion:ion:2.+' |
34 | 34 | compile 'com.android.support:design:25.3.1' |
35 | - testCompile 'junit:junit:4.12' | |
36 | 35 | compile 'com.android.support.constraint:constraint-layout:1.0.2' |
37 | 36 | compile 'com.nostra13.universalimageloader:universal-image-loader:1.9.5' |
37 | + testCompile 'junit:junit:4.12' | |
38 | 38 | } |
@@ -2,16 +2,19 @@ | ||
2 | 2 | <manifest xmlns:android="http://schemas.android.com/apk/res/android" |
3 | 3 | package="cloud.hmml.mmw"> |
4 | 4 | |
5 | + <uses-permission android:name="android.permission.INTERNET"/> | |
6 | + <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> | |
7 | + | |
5 | 8 | <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> |
6 | 9 | <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> |
7 | 10 | |
8 | 11 | <application |
12 | + android:name=".MainApplication" | |
9 | 13 | android:allowBackup="true" |
10 | 14 | android:icon="@mipmap/ic_launcher" |
11 | 15 | android:label="@string/app_name" |
12 | 16 | android:supportsRtl="true" |
13 | - android:theme="@style/AppTheme" | |
14 | - android:name="cloud.hmml.mmw.MainApplication"> | |
17 | + android:theme="@style/AppTheme.NoActionBar"> | |
15 | 18 | |
16 | 19 | <!-- |
17 | 20 | <activity android:name=".WelcomeActivity"> |
@@ -43,8 +46,10 @@ | ||
43 | 46 | </activity> |
44 | 47 | <activity |
45 | 48 | android:name=".ThemePickerActivity" |
46 | - android:label="@string/title_activity_theme_picker" | |
47 | - android:theme="@style/AppTheme.NoActionBar"></activity> | |
49 | + android:label="@string/title_activity_theme_picker" /> | |
50 | + <activity android:name=".ThemeDownloadActivity" | |
51 | + android:label="@string/title_activity_theme_download" | |
52 | + /> | |
48 | 53 | </application> |
49 | 54 | |
50 | 55 | </manifest> |
\ No newline at end of file |
@@ -9,6 +9,7 @@ import android.graphics.BitmapFactory; | ||
9 | 9 | import android.net.Uri; |
10 | 10 | |
11 | 11 | import com.nostra13.universalimageloader.cache.memory.impl.LruMemoryCache; |
12 | +import com.nostra13.universalimageloader.core.DisplayImageOptions; | |
12 | 13 | import com.nostra13.universalimageloader.core.ImageLoader; |
13 | 14 | import com.nostra13.universalimageloader.core.ImageLoaderConfiguration; |
14 | 15 | import com.nostra13.universalimageloader.core.decode.ImageDecoder; |
@@ -51,10 +52,12 @@ public class MainApplication extends Application { | ||
51 | 52 | @Override |
52 | 53 | public void onCreate() { |
53 | 54 | super.onCreate(); |
55 | + DisplayImageOptions defaultOptions = new DisplayImageOptions.Builder() | |
56 | + .cacheInMemory(true) | |
57 | + .build(); | |
54 | 58 | ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(getApplicationContext()) |
55 | - .memoryCache(new LruMemoryCache(5 * 1024 * 1024)) | |
56 | - .memoryCacheSize(5 * 1024 * 1024) | |
57 | 59 | .imageDownloader(new MmwImageDownloader(getApplicationContext())) |
60 | + .defaultDisplayImageOptions(defaultOptions) | |
58 | 61 | .build(); |
59 | 62 | ImageLoader.getInstance().init(config); |
60 | 63 |
@@ -7,13 +7,17 @@ import android.content.res.Resources; | ||
7 | 7 | import android.graphics.Bitmap; |
8 | 8 | import android.graphics.BitmapFactory; |
9 | 9 | import android.net.Uri; |
10 | +import android.support.v7.app.ActionBarDrawerToggle; | |
10 | 11 | import android.util.Log; |
11 | 12 | |
12 | 13 | import net.arnx.jsonic.JSON; |
13 | 14 | |
15 | +import java.io.BufferedInputStream; | |
16 | +import java.io.ByteArrayOutputStream; | |
14 | 17 | import java.io.File; |
15 | 18 | import java.io.FileInputStream; |
16 | 19 | import java.io.FileNotFoundException; |
20 | +import java.io.FileOutputStream; | |
17 | 21 | import java.io.IOException; |
18 | 22 | import java.io.InputStream; |
19 | 23 | import java.lang.ref.WeakReference; |
@@ -21,6 +25,10 @@ import java.util.ArrayList; | ||
21 | 25 | import java.util.HashMap; |
22 | 26 | import java.util.List; |
23 | 27 | import java.util.Map; |
28 | +import java.util.zip.ZipEntry; | |
29 | +import java.util.zip.ZipInputStream; | |
30 | + | |
31 | +import static android.R.attr.path; | |
24 | 32 | |
25 | 33 | |
26 | 34 | public class Theme { |
@@ -28,6 +36,7 @@ public class Theme { | ||
28 | 36 | public static final int TYPE_BUILTIN = 1; |
29 | 37 | public static final int TYPE_MIKU_WEATHER = 2; |
30 | 38 | public static final int TYPE_IN_STORAGE = 3; |
39 | + public static final String STORAGE_DIR = "themes"; | |
31 | 40 | |
32 | 41 | public static final String W_CLEAR_D = "d"; |
33 | 42 | public static final String W_CLEAR_N = "n"; |
@@ -38,11 +47,12 @@ public class Theme { | ||
38 | 47 | |
39 | 48 | protected int type = TYPE_BUILTIN; |
40 | 49 | public int schema_version = 1; |
41 | - public int id; | |
50 | + public int id = 0; | |
42 | 51 | public String name; |
43 | 52 | public String author; |
44 | 53 | public String url; |
45 | 54 | public String day_frame; |
55 | + public String short_desc; | |
46 | 56 | public WeakReference<Context> context; |
47 | 57 | |
48 | 58 | public static final Map<String, String> W_FNAME_MAP = new HashMap<String, String>(); |
@@ -116,6 +126,7 @@ public class Theme { | ||
116 | 126 | } |
117 | 127 | |
118 | 128 | public String getAuthor() {return author;} |
129 | + public String getShortDesc() {return short_desc;} | |
119 | 130 | public String getName() {return name;} |
120 | 131 | public Uri getUri() { |
121 | 132 | if (this.url == null || this.url.length() < 1) |
@@ -128,6 +139,114 @@ public class Theme { | ||
128 | 139 | } |
129 | 140 | } |
130 | 141 | |
142 | + public static Theme createByContentUrl(Context context, String url) { | |
143 | + return create(context, Uri.parse(url)); | |
144 | + } | |
145 | + | |
146 | + public static Theme create(Context context, Uri uri) { | |
147 | + try { | |
148 | + Theme theme = createThemeInstance(context, context.getContentResolver().openInputStream(uri)); | |
149 | + if (theme == null) | |
150 | + return null; | |
151 | + if (!extractTheme(context, theme.id, context.getContentResolver().openInputStream(uri))) | |
152 | + return null; | |
153 | + return theme; | |
154 | + } catch (FileNotFoundException e) { | |
155 | + e.printStackTrace(); | |
156 | + return null; | |
157 | + } | |
158 | + } | |
159 | + public static Theme create(Context context, File file) { | |
160 | + try { | |
161 | + Theme theme = createThemeInstance(context, new FileInputStream(file)); | |
162 | + if (theme == null) | |
163 | + return null; | |
164 | + if (!extractTheme(context, theme.id, new FileInputStream(file))) | |
165 | + return null; | |
166 | + return theme; | |
167 | + } catch (FileNotFoundException e) { | |
168 | + e.printStackTrace(); | |
169 | + return null; | |
170 | + } | |
171 | + } | |
172 | + | |
173 | + private static Theme createThemeInstance(Context context, InputStream is) { | |
174 | + ZipInputStream zis = new ZipInputStream(new BufferedInputStream(is)); | |
175 | + ZipEntry ze; | |
176 | + Theme theme = null; | |
177 | + | |
178 | + // scan theme.json | |
179 | + try { | |
180 | + while((ze = zis.getNextEntry()) != null) { | |
181 | + if (!ze.getName().equals("theme.json")) | |
182 | + continue; | |
183 | + theme = JSON.decode(zis, Theme.class); | |
184 | + if (theme.id == 0) { | |
185 | + Log.e("theme-creation", "theme.json has invalid ID, Skip!"); | |
186 | + return null; | |
187 | + } | |
188 | + | |
189 | + theme.type = TYPE_IN_STORAGE; | |
190 | + break; | |
191 | + } | |
192 | + zis.close(); | |
193 | + } catch (IOException e) { | |
194 | + e.printStackTrace(); | |
195 | + return null; | |
196 | + } | |
197 | + if (theme == null) { | |
198 | + Log.e("theme-creation", "theme.json is not found in the archive. Skip!"); | |
199 | + return null; | |
200 | + } | |
201 | + return theme; | |
202 | + } | |
203 | + | |
204 | + private static boolean extractTheme(Context context, int id, InputStream is) { | |
205 | + if (id == 0) | |
206 | + return false; | |
207 | + ZipInputStream zis = new ZipInputStream(new BufferedInputStream(is)); | |
208 | + ZipEntry ze; | |
209 | + | |
210 | + File themedir = context.getExternalFilesDir(null); | |
211 | + if (themedir == null) { | |
212 | + Log.e("theme-creation", "Cannot get extract directy. You may need to insert SD card. Abort!"); | |
213 | + return false; | |
214 | + } | |
215 | + themedir = new File(themedir, STORAGE_DIR + "/" + id); | |
216 | + | |
217 | + // Now theme json is OK, extract files... | |
218 | + Log.i("theme-creation", "Extracting theme to " + themedir.getPath()); | |
219 | + try { | |
220 | + while((ze = zis.getNextEntry()) != null) { | |
221 | + ByteArrayOutputStream baos = new ByteArrayOutputStream(); | |
222 | + byte[] buffer = new byte[16 * 1024]; | |
223 | + int count; | |
224 | + | |
225 | + String filename = ze.getName(); | |
226 | + File outfile = new File(themedir, filename); | |
227 | + (new File(outfile.getParent())).mkdirs(); | |
228 | + FileOutputStream fout = new FileOutputStream(outfile); | |
229 | + | |
230 | + while((count = zis.read(buffer)) != -1) { | |
231 | + baos.write(buffer, 0, count); | |
232 | + byte[] bytes = baos.toByteArray(); | |
233 | + fout.write(bytes); | |
234 | + baos.reset(); | |
235 | + } | |
236 | + | |
237 | + fout.close(); | |
238 | + zis.closeEntry(); | |
239 | + } | |
240 | + zis.close(); | |
241 | + | |
242 | + } catch (IOException e) { | |
243 | + e.printStackTrace(); | |
244 | + return false; | |
245 | + } | |
246 | + Log.d("theme-creation", "Theme extract has been completed!"); | |
247 | + return true; | |
248 | + } | |
249 | + | |
131 | 250 | public static List<Theme> getAll(Context context) { |
132 | 251 | List<Theme> list = new ArrayList<Theme>(); |
133 | 252 |
@@ -139,10 +258,10 @@ public class Theme { | ||
139 | 258 | // On storage |
140 | 259 | File baseDir = context.getExternalFilesDir(null); |
141 | 260 | if (baseDir != null) { |
142 | - File themesDir = new File(baseDir, "themes"); | |
261 | + File themesDir = new File(baseDir, STORAGE_DIR); | |
143 | 262 | if (themesDir.isDirectory()) { |
144 | 263 | for (String tid: themesDir.list()) { |
145 | - File theme_def = new File(baseDir, "themes/" + tid + "/theme.json"); | |
264 | + File theme_def = new File(baseDir, STORAGE_DIR + "/" + tid + "/theme.json"); | |
146 | 265 | if (!theme_def.exists()) |
147 | 266 | continue; |
148 | 267 | try { |
@@ -200,7 +319,7 @@ public class Theme { | ||
200 | 319 | return getMikuWeatherTheme(context); |
201 | 320 | case TYPE_BUILTIN: |
202 | 321 | try { |
203 | - istream = context.getAssets().open("themes/" + themeId + "/theme.json"); | |
322 | + istream = context.getAssets().open(STORAGE_DIR + "/" + themeId + "/theme.json"); | |
204 | 323 | } catch (IOException e) { |
205 | 324 | e.printStackTrace(); |
206 | 325 | return null; |
@@ -211,11 +330,11 @@ public class Theme { | ||
211 | 330 | if (baseDir == null) |
212 | 331 | throw new LoadFailed("Directory not found: "+ baseDir.getPath()); |
213 | 332 | |
214 | - File themesDir = new File(baseDir, "themes"); | |
333 | + File themesDir = new File(baseDir, STORAGE_DIR); | |
215 | 334 | if (!themesDir.isDirectory()) |
216 | 335 | throw new LoadFailed("Themes dir is not found:" + themesDir.getPath()); |
217 | 336 | |
218 | - File theme_def = new File(baseDir, "themes/" + themeId + "/theme.json"); | |
337 | + File theme_def = new File(baseDir, STORAGE_DIR + "/" + themeId + "/theme.json"); | |
219 | 338 | if (!theme_def.exists()) |
220 | 339 | throw new LoadFailed("Theme fef file not found:" + theme_def.getPath()); |
221 | 340 |
@@ -252,7 +371,7 @@ public class Theme { | ||
252 | 371 | return null; |
253 | 372 | return "mikuweather-icon:tenki_"+icon_name; |
254 | 373 | } else if (type == TYPE_BUILTIN) { |
255 | - return "assets://themes/" + id + "/" + W_FNAME_MAP.get(iconIdent); | |
374 | + return "assets://" + STORAGE_DIR + "/" + id + "/" + W_FNAME_MAP.get(iconIdent); | |
256 | 375 | } else if (type == TYPE_IN_STORAGE) { |
257 | 376 | File baseDir = getContext().getExternalFilesDir(null); |
258 | 377 | if (baseDir == null) { |
@@ -260,7 +379,7 @@ public class Theme { | ||
260 | 379 | return null; |
261 | 380 | } |
262 | 381 | |
263 | - File theme_def = new File(baseDir, "themes/" + id + "/" + W_FNAME_MAP.get(iconIdent)); | |
382 | + File theme_def = new File(baseDir, STORAGE_DIR + "/" + id + "/" + W_FNAME_MAP.get(iconIdent)); | |
264 | 383 | if (!theme_def.exists()) { |
265 | 384 | Log.d("theme", "Theme fef file not found:" + theme_def.getPath()); |
266 | 385 | return null; |
@@ -280,7 +399,7 @@ public class Theme { | ||
280 | 399 | return getMikuWeatherBitmap("tenki_"+icon_name); |
281 | 400 | } else if (type == TYPE_BUILTIN) { |
282 | 401 | InputStream istream; |
283 | - String asset_path = "themes/" + id + "/" + W_FNAME_MAP.get(iconIdent); | |
402 | + String asset_path = STORAGE_DIR + "/" + id + "/" + W_FNAME_MAP.get(iconIdent); | |
284 | 403 | try { |
285 | 404 | istream = getContext().getAssets().open(asset_path); |
286 | 405 | } catch (IOException e) { |
@@ -296,7 +415,7 @@ public class Theme { | ||
296 | 415 | return null; |
297 | 416 | } |
298 | 417 | |
299 | - File theme_def = new File(baseDir, "themes/" + id + "/" + W_FNAME_MAP.get(iconIdent)); | |
418 | + File theme_def = new File(baseDir, STORAGE_DIR + "/" + id + "/" + W_FNAME_MAP.get(iconIdent)); | |
300 | 419 | if (!theme_def.exists()) { |
301 | 420 | Log.d("theme", "Theme fef file not found:" + theme_def.getPath()); |
302 | 421 | return null; |
@@ -0,0 +1,342 @@ | ||
1 | +package cloud.hmml.mmw; | |
2 | + | |
3 | +import android.app.DownloadManager; | |
4 | +import android.content.BroadcastReceiver; | |
5 | +import android.content.Context; | |
6 | +import android.content.Intent; | |
7 | +import android.content.IntentFilter; | |
8 | +import android.database.Cursor; | |
9 | +import android.graphics.Bitmap; | |
10 | +import android.net.Uri; | |
11 | +import android.os.AsyncTask; | |
12 | +import android.os.ParcelFileDescriptor; | |
13 | +import android.support.annotation.LayoutRes; | |
14 | +import android.support.annotation.NonNull; | |
15 | +import android.support.annotation.Nullable; | |
16 | +import android.support.v4.view.MenuItemCompat; | |
17 | +import android.support.v7.app.AppCompatActivity; | |
18 | +import android.os.Bundle; | |
19 | +import android.support.v7.widget.SearchView; | |
20 | +import android.support.v7.widget.Toolbar; | |
21 | +import android.util.Log; | |
22 | +import android.view.LayoutInflater; | |
23 | +import android.view.Menu; | |
24 | +import android.view.MenuInflater; | |
25 | +import android.view.MenuItem; | |
26 | +import android.view.View; | |
27 | +import android.view.ViewGroup; | |
28 | +import android.widget.AdapterView; | |
29 | +import android.widget.ArrayAdapter; | |
30 | +import android.widget.ImageView; | |
31 | +import android.widget.ListView; | |
32 | +import android.widget.TextView; | |
33 | +import android.widget.Toast; | |
34 | + | |
35 | +import com.koushikdutta.ion.Ion; | |
36 | +import com.nostra13.universalimageloader.core.DisplayImageOptions; | |
37 | +import com.nostra13.universalimageloader.core.ImageLoader; | |
38 | +import com.nostra13.universalimageloader.core.assist.FailReason; | |
39 | +import com.nostra13.universalimageloader.core.listener.ImageLoadingListener; | |
40 | + | |
41 | +import net.arnx.jsonic.JSON; | |
42 | + | |
43 | +import java.io.BufferedReader; | |
44 | +import java.io.File; | |
45 | +import java.io.FileInputStream; | |
46 | +import java.io.FileNotFoundException; | |
47 | +import java.io.FileReader; | |
48 | +import java.io.IOException; | |
49 | +import java.io.InputStream; | |
50 | +import java.math.BigDecimal; | |
51 | +import java.math.BigInteger; | |
52 | +import java.security.MessageDigest; | |
53 | +import java.security.NoSuchAlgorithmException; | |
54 | +import java.util.ArrayList; | |
55 | +import java.util.Arrays; | |
56 | +import java.util.HashMap; | |
57 | +import java.util.concurrent.ExecutionException; | |
58 | + | |
59 | +public class ThemeDownloadActivity extends AppCompatActivity { | |
60 | + | |
61 | + static class RemoteTheme { | |
62 | + public int id; | |
63 | + public String archive_url; | |
64 | + public String archive_digest; | |
65 | + public String name; | |
66 | + public String author; | |
67 | + public String url; | |
68 | + public String short_desc; | |
69 | + public String preview1; | |
70 | + public String preview2; | |
71 | + public String preview3; | |
72 | + public String preview4; | |
73 | + | |
74 | + public boolean checkDigest(File file) { | |
75 | + FileInputStream is = null; | |
76 | + byte[] buffer = new byte[64 * 1024]; | |
77 | + int readbytes = 0; | |
78 | + try { | |
79 | + MessageDigest digest = MessageDigest.getInstance("MD5"); | |
80 | + is = new FileInputStream(file); | |
81 | + do { | |
82 | + readbytes = is.read(buffer); | |
83 | + if (readbytes > 0) | |
84 | + digest.update(buffer, 0, readbytes); | |
85 | + } while (readbytes > 0); | |
86 | + is.close(); | |
87 | + StringBuffer hexdigest = new StringBuffer(); | |
88 | + for (byte b: digest.digest()) { | |
89 | + hexdigest.append(String.format("%02x", b & 0xFF)); | |
90 | + } | |
91 | + Log.d("theme-downloader", "Target file hex digest: "+hexdigest.toString() + ", expected: "+ archive_digest); | |
92 | + if (!hexdigest.toString().toLowerCase().equals(archive_digest.toLowerCase())) | |
93 | + return false; | |
94 | + Log.d("theme-downloader", "Digest matched."); | |
95 | + return true; | |
96 | + } catch (FileNotFoundException e) { | |
97 | + e.printStackTrace(); | |
98 | + return false; | |
99 | + } catch (NoSuchAlgorithmException e) { | |
100 | + e.printStackTrace(); | |
101 | + return false; | |
102 | + } catch (IOException e) { | |
103 | + e.printStackTrace(); | |
104 | + return false; | |
105 | + } finally { | |
106 | + try { | |
107 | + if (is != null) | |
108 | + is.close(); | |
109 | + } catch (IOException e) { | |
110 | + // ignore | |
111 | + } | |
112 | + | |
113 | + } | |
114 | + | |
115 | + } | |
116 | + } | |
117 | + | |
118 | + private SearchView mSearchView; | |
119 | + private ListView listView; | |
120 | + private RemoteThemeAdapter adapter; | |
121 | + | |
122 | + static class RemoteThemeLoader extends AsyncTask<RemoteThemeAdapter, Void, ArrayList<Theme>> { | |
123 | + private RemoteThemeAdapter adapter; | |
124 | + | |
125 | + @Override | |
126 | + protected ArrayList doInBackground(RemoteThemeAdapter... params) { | |
127 | + this.adapter = params[0]; | |
128 | + return adapter.getJson(); | |
129 | + } | |
130 | + | |
131 | + @Override | |
132 | + protected void onPostExecute(ArrayList arrayList) { | |
133 | + super.onPostExecute(arrayList); | |
134 | + if (arrayList != null) | |
135 | + adapter.addAll(arrayList); | |
136 | + } | |
137 | + } | |
138 | + | |
139 | + static class RemoteThemeAdapter extends ArrayAdapter { | |
140 | + static final String BASE_URL = "http://tempest.private.nemui.org:3000/"; | |
141 | + String query; | |
142 | + int page = 0; | |
143 | + | |
144 | + public RemoteThemeAdapter(@NonNull Context context, @LayoutRes int resource) { | |
145 | + super(context, resource); | |
146 | + } | |
147 | + | |
148 | + public int getTotal() { | |
149 | + return total; | |
150 | + } | |
151 | + | |
152 | + int total = 0; | |
153 | + | |
154 | + public int getPage() { | |
155 | + return page; | |
156 | + } | |
157 | + | |
158 | + public String getQuery() { | |
159 | + return query; | |
160 | + } | |
161 | + | |
162 | + public void setQuery(String query) { | |
163 | + this.query = query; | |
164 | + } | |
165 | + | |
166 | + public void refresh() { | |
167 | + clear(); | |
168 | + new RemoteThemeLoader().execute(this); | |
169 | + } | |
170 | + | |
171 | + @NonNull | |
172 | + @Override | |
173 | + public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) { | |
174 | + if (convertView == null) { | |
175 | + convertView = LayoutInflater.from(getContext()).inflate(R.layout.theme_item, parent, false); | |
176 | + } | |
177 | + | |
178 | + RemoteTheme theme = (RemoteTheme) getItem(position); | |
179 | + | |
180 | + ImageView image1 = (ImageView) convertView.findViewById(R.id.theme_image1); | |
181 | + ImageView image2 = (ImageView) convertView.findViewById(R.id.theme_image2); | |
182 | + ImageView image3 = (ImageView) convertView.findViewById(R.id.theme_image3); | |
183 | + ImageView image4 = (ImageView) convertView.findViewById(R.id.theme_image4); | |
184 | + TextView name = (TextView) convertView.findViewById(R.id.theme_name); | |
185 | + TextView author = (TextView) convertView.findViewById(R.id.theme_author); | |
186 | + TextView desc = (TextView) convertView.findViewById(R.id.theme_desc); | |
187 | + | |
188 | + ImageLoader imageloader = ImageLoader.getInstance(); | |
189 | + DisplayImageOptions options = new DisplayImageOptions.Builder() | |
190 | + .cacheOnDisk(true) | |
191 | + .build(); | |
192 | + image1.setImageDrawable(null); | |
193 | + image2.setImageDrawable(null); | |
194 | + image3.setImageDrawable(null); | |
195 | + image4.setImageDrawable(null); | |
196 | + imageloader.displayImage(theme.preview1, image1); | |
197 | + imageloader.displayImage(theme.preview2, image2); | |
198 | + imageloader.displayImage(theme.preview3, image3); | |
199 | + imageloader.displayImage(theme.preview4, image4); | |
200 | + | |
201 | + name.setText(theme.name); | |
202 | + author.setText(theme.author); | |
203 | + desc.setText(theme.short_desc); | |
204 | + | |
205 | + return convertView; | |
206 | + } | |
207 | + private ArrayList<RemoteTheme> getJson() { | |
208 | + try { | |
209 | + InputStream is = Ion.with(this.getContext()) | |
210 | + .load(BASE_URL + "themes.json") | |
211 | + .setHeader("X-Requested-With", "Android cloud.hmml.mmw") | |
212 | + .asInputStream() | |
213 | + .get(); | |
214 | + return new ArrayList(Arrays.asList(JSON.decode(is, RemoteTheme[].class))); | |
215 | + } catch (InterruptedException e) { | |
216 | + Log.e("download-adapter", "Exception in fetching json: " + e.getMessage()); | |
217 | + e.printStackTrace(); | |
218 | + return null; | |
219 | + } catch (ExecutionException e) { | |
220 | + Log.e("download-adapter", "ExecutionException in fetching json: "+e.getMessage()); | |
221 | + e.printStackTrace(); | |
222 | + return null; | |
223 | + } catch (IOException e) { | |
224 | + Log.e("download-adapter", "IOException in fetching json: "+e.getMessage()); | |
225 | + e.printStackTrace(); | |
226 | + return null; | |
227 | + } | |
228 | + } | |
229 | + } | |
230 | + | |
231 | + | |
232 | + @Override | |
233 | + protected void onCreate(Bundle savedInstanceState) { | |
234 | + super.onCreate(savedInstanceState); | |
235 | + setContentView(R.layout.activity_theme_download); | |
236 | + | |
237 | + Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); | |
238 | + setSupportActionBar(toolbar); | |
239 | + | |
240 | + adapter = new RemoteThemeAdapter(this, R.layout.theme_item); | |
241 | + | |
242 | + listView = (ListView) findViewById(R.id.download_theme_list); | |
243 | + listView.setAdapter(adapter); | |
244 | + | |
245 | + listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { | |
246 | + @Override | |
247 | + public void onItemClick(AdapterView<?> parent, View view, int position, long id) { | |
248 | + final RemoteTheme rtheme = (RemoteTheme) parent.getAdapter().getItem(position); | |
249 | + | |
250 | + if (getApplicationContext().getExternalCacheDir() == null) { | |
251 | + Toast.makeText(getApplicationContext(), "Please insert SD card", Toast.LENGTH_LONG).show(); | |
252 | + return; | |
253 | + } | |
254 | + final File file = new File(getApplicationContext().getExternalCacheDir(), "/theme-" + rtheme.id + ".zip"); | |
255 | + Log.d("td", "path:" + file.getPath()); | |
256 | + (new File(file.getParent())).mkdirs(); | |
257 | + | |
258 | + DownloadManager.Request request = new DownloadManager.Request(Uri.parse(rtheme.archive_url)); | |
259 | + request.setTitle(rtheme.name); | |
260 | + request.setMimeType("application/zip"); | |
261 | + request.setVisibleInDownloadsUi(false); | |
262 | + request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_MOBILE | DownloadManager.Request.NETWORK_WIFI); | |
263 | + request.setDestinationUri(Uri.fromFile(file)); | |
264 | + final DownloadManager downloadManager = (DownloadManager) getSystemService(DOWNLOAD_SERVICE); | |
265 | + final long queueId = downloadManager.enqueue(request); | |
266 | + | |
267 | + final BroadcastReceiver reciever = new BroadcastReceiver() { | |
268 | + @Override | |
269 | + public void onReceive(Context context, Intent intent) { | |
270 | + String action = intent.getAction(); | |
271 | + if (DownloadManager.ACTION_DOWNLOAD_COMPLETE.equals(action)) { | |
272 | + long downloadId = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, 0); | |
273 | + DownloadManager.Query query = new DownloadManager.Query(); | |
274 | + if (queueId != downloadId) | |
275 | + return; | |
276 | + query.setFilterById(downloadId); | |
277 | + Cursor c = downloadManager.query(query); | |
278 | + if (c.moveToFirst()) { | |
279 | + for (int i = 0; i < c.getColumnCount(); i ++) { | |
280 | + Log.d("dm", "column: "+c.getColumnName(i) + ", value:" + c.getString(i)); | |
281 | + } | |
282 | + int columnIndex = c.getColumnIndex(DownloadManager.COLUMN_STATUS); | |
283 | + if (DownloadManager.STATUS_SUCCESSFUL == c.getInt(columnIndex)) { | |
284 | + String uriString = c.getString(c.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI)); | |
285 | + Uri uri = Uri.parse(uriString); | |
286 | + Log.d("theme-downloader", "download complete:" + uriString); | |
287 | + | |
288 | + if (!rtheme.checkDigest(file)) { | |
289 | + Log.e("theme-downloader", "Digest missmatch! Rejecting file: "+file.getPath()); | |
290 | + downloadManager.remove(downloadId); | |
291 | + return; | |
292 | + } | |
293 | + | |
294 | + Theme.create(getApplicationContext(), downloadManager.getUriForDownloadedFile(downloadId)); | |
295 | + } | |
296 | + } | |
297 | + downloadManager.remove(downloadId); | |
298 | + c.close(); | |
299 | + context.unregisterReceiver(this); | |
300 | + } | |
301 | + } | |
302 | + }; | |
303 | + getApplicationContext().registerReceiver(reciever, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE)); | |
304 | + } | |
305 | + }); | |
306 | + | |
307 | + adapter.refresh(); | |
308 | + } | |
309 | + | |
310 | + @Override | |
311 | + public boolean onCreateOptionsMenu(Menu menu) { | |
312 | + // Set Menu | |
313 | + MenuInflater inflater = getMenuInflater(); | |
314 | + inflater.inflate(R.menu.menu_search, menu); | |
315 | + | |
316 | + MenuItem menuItem = menu.findItem(R.id.toolbar_menu_search); | |
317 | + | |
318 | + mSearchView = (SearchView) MenuItemCompat.getActionView(menuItem); | |
319 | + | |
320 | + // whether display Magnifying Glass Icon at first | |
321 | + mSearchView.setIconifiedByDefault(true); | |
322 | + | |
323 | + // whether display Submit Button | |
324 | + mSearchView.setSubmitButtonEnabled(false); | |
325 | + | |
326 | + mSearchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() { | |
327 | + @Override | |
328 | + public boolean onQueryTextSubmit(String query) { | |
329 | + return true; | |
330 | + } | |
331 | + | |
332 | + @Override | |
333 | + public boolean onQueryTextChange(String newText) { | |
334 | + //suggestion_search(newText); | |
335 | + return true; | |
336 | + //return false; | |
337 | + } | |
338 | + }); | |
339 | + | |
340 | + return super.onCreateOptionsMenu(menu); | |
341 | + } | |
342 | +} |
@@ -53,20 +53,26 @@ public class ThemePickerActivity extends AppCompatActivity { | ||
53 | 53 | // Lookup view for data population |
54 | 54 | TextView name = (TextView) convertView.findViewById(R.id.theme_name); |
55 | 55 | TextView author = (TextView) convertView.findViewById(R.id.theme_author); |
56 | + TextView desc = (TextView) convertView.findViewById(R.id.theme_desc); | |
56 | 57 | ImageView image1 = (ImageView) convertView.findViewById(R.id.theme_image1); |
57 | 58 | ImageView image2 = (ImageView) convertView.findViewById(R.id.theme_image2); |
58 | 59 | ImageView image3 = (ImageView) convertView.findViewById(R.id.theme_image3); |
60 | + ImageView image4 = (ImageView) convertView.findViewById(R.id.theme_image4); | |
59 | 61 | |
60 | 62 | |
61 | 63 | // Populate the data into the template view using the data object |
62 | - name.setText(theme.name); | |
63 | - author.setText(theme.author); | |
64 | + name.setText(theme.getName()); | |
65 | + author.setText(theme.getAuthor()); | |
66 | + desc.setText(theme.getShortDesc()); | |
67 | + image1.setImageDrawable(null); | |
68 | + image2.setImageDrawable(null); | |
69 | + image3.setImageDrawable(null); | |
70 | + image4.setImageDrawable(null); | |
64 | 71 | ImageLoader imageLoader = ImageLoader.getInstance(); |
65 | - imageLoader.displayImage(theme.getIconURL(Theme.W_CLEAR_D + Theme.W_RAIN), image1); | |
66 | - imageLoader.displayImage(theme.getIconURL(Theme.W_SNOW + Theme.W_CLEAR_N), image2); | |
67 | - imageLoader.displayImage(theme.getIconURL(Theme.W_CLOUD + Theme.W_THUNDER), image3); | |
68 | - image2.setVisibility(View.VISIBLE); | |
69 | - image3.setVisibility(View.VISIBLE); | |
72 | + imageLoader.displayImage(theme.getIconURL(Theme.W_CLEAR_D), image1); | |
73 | + imageLoader.displayImage(theme.getIconURL(Theme.W_CLEAR_D + Theme.W_RAIN), image2); | |
74 | + imageLoader.displayImage(theme.getIconURL(Theme.W_SNOW + Theme.W_CLEAR_N), image3); | |
75 | + imageLoader.displayImage(theme.getIconURL(Theme.W_CLOUD + Theme.W_THUNDER), image4); | |
70 | 76 | |
71 | 77 | Drawable bg; |
72 | 78 | if (theme.day_frame != null && theme.day_frame.equals("builtin/white")) { |
@@ -77,6 +83,7 @@ public class ThemePickerActivity extends AppCompatActivity { | ||
77 | 83 | image1.setBackground(bg); |
78 | 84 | image2.setBackground(bg); |
79 | 85 | image3.setBackground(bg); |
86 | + image4.setBackground(bg); | |
80 | 87 | |
81 | 88 | if (theme.getUri() != null) { |
82 | 89 | author.setTextColor(Color.BLUE); |
@@ -95,7 +102,7 @@ public class ThemePickerActivity extends AppCompatActivity { | ||
95 | 102 | } |
96 | 103 | |
97 | 104 | @Override |
98 | - protected void onCreate(Bundle savedInstanceState) { | |
105 | + protected void onCreate(final Bundle savedInstanceState) { | |
99 | 106 | super.onCreate(savedInstanceState); |
100 | 107 | setContentView(R.layout.activity_theme_picker); |
101 | 108 | Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); |
@@ -105,9 +112,7 @@ public class ThemePickerActivity extends AppCompatActivity { | ||
105 | 112 | to_download.setOnClickListener(new View.OnClickListener() { |
106 | 113 | @Override |
107 | 114 | public void onClick(View view) { |
108 | - Snackbar.make(view, "ダウンロード機能は準備中です。暫くお待ちください。", Snackbar.LENGTH_LONG) | |
109 | - .setAction("Action", null).show(); | |
110 | - // TODO: implement download activity | |
115 | + startActivity(new Intent(ThemePickerActivity.this, ThemeDownloadActivity.class)); | |
111 | 116 | } |
112 | 117 | }); |
113 | 118 |
@@ -41,9 +41,10 @@ public class WeatherDispWidgetConfigureActivity extends Activity { | ||
41 | 41 | private Button mBtnFetch; |
42 | 42 | private TextView mTxtReportedAt; |
43 | 43 | private Button mBtnThemeChange; |
44 | - private ImageView mThemeImage1; | |
44 | + private ImageView mThemeImage; | |
45 | 45 | private TextView mThemeName; |
46 | 46 | private TextView mThemeAuthor; |
47 | + private TextView mThemeDesc; | |
47 | 48 | |
48 | 49 | View.OnClickListener mOnClickListener = new View.OnClickListener() { |
49 | 50 | public void onClick(View v) { |
@@ -107,11 +108,11 @@ public class WeatherDispWidgetConfigureActivity extends Activity { | ||
107 | 108 | mTxtReportedAt.setText(DateFormat.getDateTimeInstance().format(upd)); |
108 | 109 | |
109 | 110 | Theme theme = conf.getTheme(); |
110 | - mThemeImage1.setImageBitmap(theme.getIcon(Theme.W_CLEAR_D)); | |
111 | + mThemeImage.setImageBitmap(theme.getIcon(Theme.W_CLEAR_D)); | |
111 | 112 | if (theme.day_frame != null && theme.day_frame.equals("builtin/white")) { |
112 | - mThemeImage1.setBackground(ContextCompat.getDrawable(this, R.drawable.day_frame_white)); | |
113 | + mThemeImage.setBackground(ContextCompat.getDrawable(this, R.drawable.day_frame_white)); | |
113 | 114 | } else { |
114 | - mThemeImage1.setBackground(ContextCompat.getDrawable(this, R.drawable.day_frame_black)); | |
115 | + mThemeImage.setBackground(ContextCompat.getDrawable(this, R.drawable.day_frame_black)); | |
115 | 116 | } |
116 | 117 | mThemeName.setText(theme.getName()); |
117 | 118 | mThemeAuthor.setText(theme.getAuthor()); |
@@ -128,6 +129,7 @@ public class WeatherDispWidgetConfigureActivity extends Activity { | ||
128 | 129 | } |
129 | 130 | }); |
130 | 131 | } |
132 | + mThemeDesc.setText(theme.getShortDesc()); | |
131 | 133 | } |
132 | 134 | |
133 | 135 | @Override |
@@ -165,9 +167,10 @@ public class WeatherDispWidgetConfigureActivity extends Activity { | ||
165 | 167 | mTxtReportedAt = (TextView) findViewById(R.id.txt_last_forecast); |
166 | 168 | mBtnFetch = (Button) findViewById(R.id.btn_fetch); |
167 | 169 | mBtnThemeChange = (Button) findViewById(R.id.btn_theme_change); |
168 | - mThemeImage1 = (ImageView) findViewById(R.id.theme_image1); | |
169 | - mThemeName = (TextView) findViewById(R.id.theme_name); | |
170 | - mThemeAuthor = (TextView) findViewById(R.id.theme_author); | |
170 | + mThemeImage = (ImageView) findViewById(R.id.theme_cur_image); | |
171 | + mThemeName = (TextView) findViewById(R.id.theme_cur_name); | |
172 | + mThemeAuthor = (TextView) findViewById(R.id.theme_cur_author); | |
173 | + mThemeDesc = (TextView) findViewById(R.id.theme_cur_desc); | |
171 | 174 | |
172 | 175 | mChkAutoLocation.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { |
173 | 176 | @Override |
@@ -0,0 +1,46 @@ | ||
1 | +<?xml version="1.0" encoding="utf-8"?> | |
2 | +<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" | |
3 | + xmlns:app="http://schemas.android.com/apk/res-auto" | |
4 | + xmlns:tools="http://schemas.android.com/tools" | |
5 | + android:layout_width="match_parent" | |
6 | + android:layout_height="match_parent" | |
7 | + tools:context="cloud.hmml.mmw.ThemeDownloadActivity"> | |
8 | + | |
9 | + <android.support.design.widget.AppBarLayout | |
10 | + android:layout_width="match_parent" | |
11 | + android:layout_height="wrap_content" | |
12 | + android:theme="@style/AppTheme.AppBarOverlay" | |
13 | + tools:layout_editor_absoluteX="8dp" | |
14 | + tools:layout_editor_absoluteY="0dp"> | |
15 | + | |
16 | + <android.support.v7.widget.Toolbar | |
17 | + android:id="@+id/toolbar" | |
18 | + android:layout_width="match_parent" | |
19 | + android:layout_height="?attr/actionBarSize" | |
20 | + android:background="?attr/colorPrimary" | |
21 | + app:popupTheme="@style/AppTheme.PopupOverlay" /> | |
22 | + | |
23 | + </android.support.design.widget.AppBarLayout> | |
24 | + | |
25 | + <LinearLayout | |
26 | + xmlns:tools="http://schemas.android.com/tools" | |
27 | + android:layout_width="match_parent" | |
28 | + android:layout_height="match_parent" | |
29 | + app:layout_behavior="@string/appbar_scrolling_view_behavior" | |
30 | + > | |
31 | + | |
32 | + <ListView | |
33 | + android:id="@+id/download_theme_list" | |
34 | + android:layout_width="match_parent" | |
35 | + android:layout_height="match_parent" | |
36 | + android:layout_marginEnd="8dp" | |
37 | + android:layout_marginLeft="8dp" | |
38 | + android:layout_marginRight="8dp" | |
39 | + android:layout_marginStart="8dp" | |
40 | + app:layout_constraintLeft_toLeftOf="parent" | |
41 | + app:layout_constraintRight_toRightOf="parent" | |
42 | + tools:layout_editor_absoluteY="8dp" /> | |
43 | + </LinearLayout> | |
44 | + | |
45 | + | |
46 | +</android.support.design.widget.CoordinatorLayout> |
@@ -20,8 +20,22 @@ | ||
20 | 20 | |
21 | 21 | </android.support.design.widget.AppBarLayout> |
22 | 22 | |
23 | - <include layout="@layout/content_theme_picker" | |
24 | - android:layout_height="512dp" /> | |
23 | + <LinearLayout | |
24 | + android:layout_width="match_parent" | |
25 | + android:layout_height="match_parent" | |
26 | + app:layout_behavior="@string/appbar_scrolling_view_behavior" | |
27 | + > | |
28 | + | |
29 | + <ListView | |
30 | + android:id="@+id/cur_theme_list" | |
31 | + android:layout_width="match_parent" | |
32 | + android:layout_height="match_parent" | |
33 | + android:layout_marginEnd="8dp" | |
34 | + android:layout_marginLeft="8dp" | |
35 | + android:layout_marginRight="8dp" | |
36 | + android:layout_marginStart="8dp" | |
37 | + tools:layout_editor_absoluteY="8dp" /> | |
38 | + </LinearLayout> | |
25 | 39 | |
26 | 40 | <android.support.design.widget.FloatingActionButton |
27 | 41 | android:id="@+id/btn_to_download" |
@@ -29,6 +43,7 @@ | ||
29 | 43 | android:layout_height="wrap_content" |
30 | 44 | android:layout_gravity="bottom|end" |
31 | 45 | android:layout_margin="@dimen/fab_margin" |
46 | + app:elevation="8dp" | |
32 | 47 | app:srcCompat="@android:drawable/stat_sys_download" /> |
33 | 48 | |
34 | 49 | </android.support.design.widget.CoordinatorLayout> |
@@ -1,20 +0,0 @@ | ||
1 | -<?xml version="1.0" encoding="utf-8"?> | |
2 | -<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" | |
3 | - xmlns:app="http://schemas.android.com/apk/res-auto" | |
4 | - xmlns:tools="http://schemas.android.com/tools" | |
5 | - android:layout_width="match_parent" | |
6 | - android:layout_height="match_parent" | |
7 | - app:layout_behavior="@string/appbar_scrolling_view_behavior" | |
8 | - tools:context="cloud.hmml.mmw.ThemePickerActivity" | |
9 | - tools:showIn="@layout/activity_theme_picker"> | |
10 | - | |
11 | - <ListView | |
12 | - android:id="@+id/cur_theme_list" | |
13 | - android:layout_width="match_parent" | |
14 | - android:layout_height="match_parent" | |
15 | - tools:layout_editor_absoluteY="8dp" | |
16 | - android:layout_marginRight="8dp" | |
17 | - app:layout_constraintRight_toRightOf="parent" | |
18 | - android:layout_marginLeft="8dp" | |
19 | - app:layout_constraintLeft_toLeftOf="parent" /> | |
20 | -</android.support.constraint.ConstraintLayout> |
@@ -9,8 +9,8 @@ | ||
9 | 9 | |
10 | 10 | <ImageView |
11 | 11 | android:id="@+id/theme_image1" |
12 | - android:layout_width="72dp" | |
13 | - android:layout_height="72dp" | |
12 | + android:layout_width="52dp" | |
13 | + android:layout_height="52dp" | |
14 | 14 | android:layout_weight="1" |
15 | 15 | android:background="@android:drawable/editbox_dropdown_dark_frame" |
16 | 16 | android:focusable="false" |
@@ -18,33 +18,42 @@ | ||
18 | 18 | |
19 | 19 | <ImageView |
20 | 20 | android:id="@+id/theme_image2" |
21 | - android:layout_width="72dp" | |
22 | - android:layout_height="72dp" | |
21 | + android:layout_width="52dp" | |
22 | + android:layout_height="52dp" | |
23 | 23 | android:layout_toRightOf="@+id/theme_image1" |
24 | 24 | android:layout_weight="1" |
25 | 25 | android:background="@android:drawable/editbox_dropdown_dark_frame" |
26 | 26 | android:focusable="false" |
27 | - android:visibility="gone" | |
28 | 27 | app:srcCompat="@android:drawable/btn_dialog" /> |
29 | 28 | |
30 | 29 | <ImageView |
31 | 30 | android:id="@+id/theme_image3" |
32 | - android:layout_width="72dp" | |
33 | - android:layout_height="72dp" | |
34 | - android:layout_toRightOf="@+id/theme_image2" | |
31 | + android:layout_width="52dp" | |
32 | + android:layout_height="52dp" | |
33 | + android:layout_below="@+id/theme_image1" | |
34 | + android:layout_weight="1" | |
35 | + android:background="@android:drawable/editbox_dropdown_dark_frame" | |
36 | + android:focusable="false" | |
37 | + app:srcCompat="@android:drawable/btn_dialog" /> | |
38 | + | |
39 | + <ImageView | |
40 | + android:id="@+id/theme_image4" | |
41 | + android:layout_width="52dp" | |
42 | + android:layout_height="52dp" | |
43 | + android:layout_alignTop="@+id/theme_image3" | |
44 | + android:layout_toRightOf="@+id/theme_image3" | |
35 | 45 | android:layout_weight="1" |
36 | 46 | android:background="@android:drawable/editbox_dropdown_dark_frame" |
37 | 47 | android:focusable="false" |
38 | - android:visibility="gone" | |
39 | 48 | app:srcCompat="@android:drawable/btn_dialog" /> |
40 | 49 | |
41 | 50 | <TextView |
42 | 51 | android:id="@+id/theme_author" |
43 | - android:layout_width="wrap_content" | |
52 | + android:layout_width="match_parent" | |
44 | 53 | android:layout_height="wrap_content" |
45 | 54 | android:layout_below="@+id/theme_name" |
46 | 55 | android:layout_marginLeft="8sp" |
47 | - android:layout_toRightOf="@id/theme_image3" | |
56 | + android:layout_toRightOf="@+id/theme_image2" | |
48 | 57 | android:layout_weight="1" |
49 | 58 | android:ems="10" |
50 | 59 | android:focusable="false" |
@@ -57,7 +66,7 @@ | ||
57 | 66 | android:layout_width="match_parent" |
58 | 67 | android:layout_height="wrap_content" |
59 | 68 | android:layout_marginLeft="8sp" |
60 | - android:layout_toRightOf="@+id/theme_image3" | |
69 | + android:layout_toRightOf="@+id/theme_image2" | |
61 | 70 | android:layout_weight="1" |
62 | 71 | android:ems="10" |
63 | 72 | android:focusable="false" |
@@ -65,4 +74,19 @@ | ||
65 | 74 | android:text="Name" |
66 | 75 | android:textAppearance="@style/TextAppearance.AppCompat" /> |
67 | 76 | |
77 | + <LinearLayout | |
78 | + android:id="@+id/theme_info_icons" | |
79 | + android:layout_width="match_parent" | |
80 | + android:layout_height="wrap_content" | |
81 | + android:layout_alignStart="@+id/theme_author" | |
82 | + android:layout_below="@+id/theme_author" | |
83 | + android:orientation="vertical"></LinearLayout> | |
84 | + | |
85 | + <TextView | |
86 | + android:id="@+id/theme_desc" | |
87 | + android:layout_width="wrap_content" | |
88 | + android:layout_height="wrap_content" | |
89 | + android:layout_alignLeft="@+id/theme_author" | |
90 | + android:layout_below="@+id/theme_info_icons" /> | |
91 | + | |
68 | 92 | </RelativeLayout> |
@@ -42,8 +42,6 @@ | ||
42 | 42 | android:id="@+id/btn_location_change" |
43 | 43 | android:layout_width="wrap_content" |
44 | 44 | android:layout_height="wrap_content" |
45 | - android:layout_marginRight="0dp" | |
46 | - android:layout_marginTop="0dp" | |
47 | 45 | android:text="Change" |
48 | 46 | android:visibility="gone" |
49 | 47 | app:layout_constraintRight_toRightOf="@+id/location_headline" |
@@ -80,7 +78,7 @@ | ||
80 | 78 | app:layout_constraintTop_toBottomOf="@+id/txt_location_name" /> |
81 | 79 | |
82 | 80 | <TextView |
83 | - android:id="@+id/thme_headline" | |
81 | + android:id="@+id/theme_headline" | |
84 | 82 | android:layout_width="0dp" |
85 | 83 | android:layout_height="wrap_content" |
86 | 84 | android:layout_marginLeft="16dp" |
@@ -98,48 +96,83 @@ | ||
98 | 96 | android:layout_marginStart="16dp" |
99 | 97 | android:layout_marginEnd="16dp" /> |
100 | 98 | |
101 | - <include | |
102 | - layout="@layout/theme_item" | |
99 | + <ImageView | |
100 | + android:id="@+id/theme_cur_image" | |
101 | + android:layout_width="80dp" | |
102 | + android:layout_height="80dp" | |
103 | + android:layout_marginLeft="0dp" | |
104 | + android:layout_marginTop="8dp" | |
105 | + android:layout_weight="1" | |
106 | + android:background="@android:drawable/editbox_dropdown_dark_frame" | |
107 | + android:focusable="false" | |
108 | + app:layout_constraintLeft_toLeftOf="@+id/theme_headline" | |
109 | + app:layout_constraintTop_toBottomOf="@+id/theme_headline" | |
110 | + app:srcCompat="@android:drawable/btn_dialog" /> | |
111 | + | |
112 | + <TextView | |
113 | + android:id="@+id/theme_cur_author" | |
103 | 114 | android:layout_width="0dp" |
104 | 115 | android:layout_height="wrap_content" |
105 | - tools:layout_constraintTop_creator="1" | |
106 | - app:layout_constraintTop_toBottomOf="@+id/thme_headline" | |
107 | - tools:layout_constraintLeft_creator="1" | |
108 | - app:layout_constraintLeft_toLeftOf="@+id/thme_headline" | |
109 | - android:id="@+id/include" | |
110 | - app:layout_constraintHorizontal_bias="0.0" | |
111 | - android:layout_marginTop="-1dp" | |
116 | + android:layout_marginLeft="8dp" | |
112 | 117 | android:layout_marginRight="8dp" |
113 | - app:layout_constraintRight_toRightOf="@+id/thme_headline" /> | |
118 | + android:layout_marginTop="4dp" | |
119 | + android:layout_weight="1" | |
120 | + android:ems="10" | |
121 | + android:focusable="false" | |
122 | + android:inputType="textPersonName" | |
123 | + android:linksClickable="true" | |
124 | + android:text="Name" | |
125 | + app:layout_constraintLeft_toRightOf="@+id/theme_cur_image" | |
126 | + app:layout_constraintTop_toBottomOf="@+id/theme_cur_name" | |
127 | + app:layout_constraintRight_toLeftOf="@+id/btn_theme_change" | |
128 | + app:layout_constraintHorizontal_bias="0.0" /> | |
129 | + | |
130 | + <TextView | |
131 | + android:id="@+id/theme_cur_name" | |
132 | + android:layout_width="0dp" | |
133 | + android:layout_height="wrap_content" | |
134 | + android:layout_weight="1" | |
135 | + android:ems="10" | |
136 | + android:focusable="false" | |
137 | + android:inputType="textPersonName" | |
138 | + android:text="Name" | |
139 | + android:textAppearance="@style/TextAppearance.AppCompat" | |
140 | + app:layout_constraintLeft_toRightOf="@+id/theme_cur_image" | |
141 | + android:layout_marginLeft="8dp" | |
142 | + android:layout_marginTop="7dp" | |
143 | + app:layout_constraintTop_toBottomOf="@+id/theme_headline" | |
144 | + android:layout_marginRight="8dp" | |
145 | + app:layout_constraintRight_toLeftOf="@+id/btn_theme_change" | |
146 | + app:layout_constraintHorizontal_bias="0.0" /> | |
114 | 147 | |
115 | 148 | <TextView |
116 | 149 | android:id="@+id/last_updat_headline" |
117 | 150 | android:layout_width="0dp" |
118 | 151 | android:layout_height="wrap_content" |
152 | + android:layout_marginEnd="16dp" | |
119 | 153 | android:layout_marginLeft="16dp" |
120 | 154 | android:layout_marginRight="16dp" |
155 | + android:layout_marginStart="16dp" | |
156 | + android:layout_marginTop="16dp" | |
121 | 157 | android:background="@color/colorPrimary" |
122 | 158 | android:text="@string/last_update" |
123 | 159 | android:textAppearance="@style/TextAppearance.AppCompat.Inverse" |
160 | + app:layout_constraintHorizontal_bias="0.0" | |
124 | 161 | app:layout_constraintLeft_toLeftOf="parent" |
125 | 162 | app:layout_constraintRight_toRightOf="parent" |
126 | - tools:layout_constraintLeft_creator="1" | |
127 | - tools:layout_constraintTop_creator="1" | |
128 | - app:layout_constraintHorizontal_bias="0.0" | |
129 | - android:layout_marginTop="16dp" | |
130 | - app:layout_constraintTop_toBottomOf="@+id/include" | |
131 | - android:layout_marginStart="16dp" | |
132 | - android:layout_marginEnd="16dp" /> | |
163 | + app:layout_constraintTop_toBottomOf="@+id/theme_cur_image" | |
164 | + tools:layout_constraintLeft_creator="1" /> | |
133 | 165 | |
134 | 166 | <Button |
135 | 167 | android:id="@+id/btn_theme_change" |
136 | - android:layout_width="wrap_content" | |
168 | + android:layout_width="0dp" | |
137 | 169 | android:layout_height="wrap_content" |
138 | - android:text="@string/change" | |
139 | - app:layout_constraintRight_toRightOf="@+id/thme_headline" | |
140 | - app:layout_constraintBottom_toBottomOf="@+id/include" | |
141 | 170 | android:layout_marginRight="0dp" |
142 | - android:layout_marginBottom="0dp" /> | |
171 | + android:layout_marginTop="8dp" | |
172 | + android:lines="1" | |
173 | + android:text="@string/change" | |
174 | + app:layout_constraintRight_toRightOf="@+id/theme_headline" | |
175 | + app:layout_constraintTop_toBottomOf="@+id/theme_headline" /> | |
143 | 176 | |
144 | 177 | <TextView |
145 | 178 | android:id="@+id/txt_last_update" |
@@ -148,12 +181,12 @@ | ||
148 | 181 | android:text="@android:string/unknownName" |
149 | 182 | android:textAppearance="@style/TextAppearance.AppCompat.Body1" |
150 | 183 | tools:layout_constraintLeft_creator="1" |
151 | - tools:layout_constraintRight_creator="1" | |
152 | 184 | tools:layout_constraintTop_creator="1" |
153 | 185 | app:layout_constraintLeft_toRightOf="@+id/fetch_time" |
154 | 186 | android:layout_marginLeft="8dp" |
155 | 187 | android:layout_marginTop="8dp" |
156 | - app:layout_constraintTop_toBottomOf="@+id/forecast_time" /> | |
188 | + app:layout_constraintTop_toBottomOf="@+id/forecast_time" | |
189 | + android:layout_marginStart="8dp" /> | |
157 | 190 | |
158 | 191 | <Button |
159 | 192 | android:id="@+id/btn_conf_ok" |
@@ -215,14 +248,12 @@ | ||
215 | 248 | android:layout_marginTop="8dp" |
216 | 249 | android:text="@string/update" |
217 | 250 | app:layout_constraintTop_toBottomOf="@+id/last_updat_headline" |
218 | - android:layout_marginRight="0dp" | |
219 | 251 | app:layout_constraintRight_toRightOf="@+id/last_updat_headline" /> |
220 | 252 | |
221 | 253 | <TextView |
222 | 254 | android:id="@+id/forecast_time" |
223 | 255 | android:layout_width="wrap_content" |
224 | 256 | android:layout_height="wrap_content" |
225 | - android:layout_marginLeft="0dp" | |
226 | 257 | android:text="@string/forecast_time" |
227 | 258 | app:layout_constraintLeft_toLeftOf="@+id/last_updat_headline" |
228 | 259 | android:layout_marginTop="8dp" |
@@ -232,7 +263,6 @@ | ||
232 | 263 | android:id="@+id/fetch_time" |
233 | 264 | android:layout_width="wrap_content" |
234 | 265 | android:layout_height="wrap_content" |
235 | - android:layout_marginLeft="0dp" | |
236 | 266 | android:layout_marginTop="8dp" |
237 | 267 | android:text="@string/fetch_time" |
238 | 268 | app:layout_constraintLeft_toLeftOf="@+id/forecast_time" |
@@ -247,7 +277,21 @@ | ||
247 | 277 | android:text="@android:string/unknownName" |
248 | 278 | android:textAppearance="@style/TextAppearance.AppCompat.Body1" |
249 | 279 | app:layout_constraintLeft_toRightOf="@+id/forecast_time" |
250 | - app:layout_constraintTop_toBottomOf="@+id/last_updat_headline" /> | |
280 | + app:layout_constraintTop_toBottomOf="@+id/last_updat_headline" | |
281 | + android:layout_marginStart="8dp" /> | |
282 | + | |
283 | + <TextView | |
284 | + android:id="@+id/theme_cur_desc" | |
285 | + android:layout_width="0dp" | |
286 | + android:layout_height="wrap_content" | |
287 | + android:layout_below="@+id/theme_cur_author" | |
288 | + android:layout_marginLeft="8dp" | |
289 | + android:layout_marginRight="8dp" | |
290 | + android:layout_marginTop="4dp" | |
291 | + app:layout_constraintHorizontal_bias="0.0" | |
292 | + app:layout_constraintLeft_toRightOf="@+id/theme_cur_image" | |
293 | + app:layout_constraintRight_toRightOf="parent" | |
294 | + app:layout_constraintTop_toBottomOf="@+id/theme_cur_author" /> | |
251 | 295 | |
252 | 296 | |
253 | 297 | </android.support.constraint.ConstraintLayout> |
\ No newline at end of file |
@@ -0,0 +1,12 @@ | ||
1 | +<?xml version="1.0" encoding="utf-8"?> | |
2 | +<menu xmlns:android="http://schemas.android.com/apk/res/android" | |
3 | + xmlns:app="http://schemas.android.com/apk/res-auto"> | |
4 | + | |
5 | + <item | |
6 | + android:id="@+id/toolbar_menu_search" | |
7 | + android:icon="@android:drawable/ic_menu_search" | |
8 | + android:title="Search" | |
9 | + app:actionViewClass="android.support.v7.widget.SearchView" | |
10 | + app:showAsAction="always" /> | |
11 | + | |
12 | +</menu> |
@@ -17,4 +17,5 @@ | ||
17 | 17 | <string name="location_perm_rejected">位置情報の利用が拒否されました。\n自動位置決定は利用できません。</string> |
18 | 18 | <string name="request_perm_location">予報エリアの自動決定のため、位置情報の利用許可をお願いします。</string> |
19 | 19 | <string name="title_activity_theme_picker">テーマの選択</string> |
20 | + <string name="title_activity_theme_download">テーマのダウンロード</string> | |
20 | 21 | </resources> |
\ No newline at end of file |
@@ -18,4 +18,5 @@ | ||
18 | 18 | <string name="location_perm_rejected">Permission request for location has been rejected.\nYou cannot use auto location setting.</string> |
19 | 19 | <string name="request_perm_location">Please allow to use location info to detect weather forcast area.</string> |
20 | 20 | <string name="title_activity_theme_picker">Select weather theme</string> |
21 | + <string name="title_activity_theme_download">Download themes</string> | |
21 | 22 | </resources> |