package com.dm.wallpaper.board.helpers; import android.app.WallpaperManager; import android.content.Context; import android.content.Intent; import android.content.res.Configuration; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Point; import android.graphics.RectF; import android.net.Uri; import android.os.AsyncTask; import android.os.Build; import android.os.Environment; import android.support.annotation.ColorInt; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v7.app.AppCompatActivity; import android.util.Log; import android.view.View; import android.widget.Toast; import com.afollestad.materialdialogs.MaterialDialog; import com.danimahardhika.android.helpers.core.ColorHelper; import com.danimahardhika.android.helpers.core.FileHelper; import com.danimahardhika.android.helpers.core.WindowHelper; import com.danimahardhika.cafebar.CafeBar; import com.danimahardhika.cafebar.CafeBarDuration; import com.danimahardhika.cafebar.CafeBarTheme; import com.dm.wallpaper.board.R; import com.dm.wallpaper.board.activities.WallpaperBoardActivity; import com.dm.wallpaper.board.preferences.Preferences; import com.dm.wallpaper.board.utils.ImageConfig; import com.dm.wallpaper.board.utils.LogUtil; import com.nostra13.universalimageloader.core.ImageLoader; import com.nostra13.universalimageloader.core.assist.FailReason; import com.nostra13.universalimageloader.core.assist.ImageSize; import com.nostra13.universalimageloader.core.listener.SimpleImageLoadingListener; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.InputStream; import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.URL; import static com.danimahardhika.android.helpers.permission.PermissionHelper.isStorageGranted; /* * Wallpaper Board * * Copyright (c) 2017 Dani Mahardhika * * 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. */ public class WallpaperHelper { public static final String IMAGE_EXTENSION = ".jpeg"; public static File getDefaultWallpapersDirectory(@NonNull Context context) { try { if (Preferences.get(context).getWallsDirectory().length() == 0) { return new File(Environment.getExternalStoragePublicDirectory( Environment.DIRECTORY_PICTURES) +"/"+ context.getResources().getString(R.string.app_name)); } return new File(Preferences.get(context).getWallsDirectory()); } catch (Exception e) { return new File(context.getFilesDir().toString() +"/Pictures/"+ context.getResources().getString(R.string.app_name)); } } public static String getThumbnailUrl(@NonNull Context context, String url, String thumbUrl) { if (thumbUrl.equals(url) && WallpaperBoardActivity.sRszIoAvailable) { if (thumbUrl.contains("drive.google.com")) return thumbUrl; return getRszIoThumbnailUrl(context, url); } return thumbUrl; } private static String getRszIoThumbnailUrl(@NonNull Context context, String url) { String rszIoUrl = url.replaceFirst("https://|http://", ""); ImageSize imageSize = ImageConfig.getThumbnailSize(context); return "https://rsz.io/" +rszIoUrl+ "?height=" +imageSize.getWidth(); } private static String getWallpaperUri(@NonNull Context context, String url, String filename) { if (isStorageGranted(context)) { File directory = getDefaultWallpapersDirectory(context); if (new File(directory + File.separator + filename).exists()) { return "file://" + directory + File.separator + filename; } } return url; } public static void downloadWallpaper(@NonNull Context context, @ColorInt int color, String link, String name) { File cache = ImageLoader.getInstance().getDiskCache().get(link); if (cache != null) { File target = new File(getDefaultWallpapersDirectory(context).toString() + File.separator + name + WallpaperHelper.IMAGE_EXTENSION); if (com.danimahardhika.android.helpers.core.FileHelper.copy(cache, target)) { wallpaperSaved(context, color, target); context.sendBroadcast(new Intent( Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.fromFile( new File(target.toString())))); return; } } new AsyncTask<Void, Integer, Boolean>() { MaterialDialog dialog; HttpURLConnection connection; File output; File file; int fileLength; @Override protected void onPreExecute() { super.onPreExecute(); output = WallpaperHelper.getDefaultWallpapersDirectory(context); file = new File(output.toString() + File.separator + name + WallpaperHelper.IMAGE_EXTENSION); MaterialDialog.Builder builder = new MaterialDialog.Builder(context); builder.content(R.string.wallpaper_downloading) .typeface("Font-Medium.ttf", "Font-Regular.ttf") .widgetColor(color) .progress(true, 0); dialog = builder.build(); dialog.setOnDismissListener(dialog1 -> { try { if (connection != null) connection.disconnect(); } catch (Exception ignored) {} cancel(true); }); dialog.show(); } @Override protected Boolean doInBackground(Void... voids) { while (!isCancelled()) { try { Thread.sleep(1); if (!output.exists()) if (!output.mkdirs()) return false; URL url = new URL(link); connection = (HttpURLConnection) url.openConnection(); connection.setConnectTimeout(15000); if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) { fileLength = connection.getContentLength(); InputStream stream = connection.getInputStream(); OutputStream output = new FileOutputStream(file.toString()); byte data[] = new byte[1024]; long total = 0; int count; while ((count = stream.read(data)) != -1) { total += count; if (fileLength > 0) publishProgress((int) (total * 100 / fileLength)); output.write(data, 0, count); } output.flush(); output.close(); stream.close(); return true; } } catch (Exception e) { LogUtil.e(Log.getStackTraceString(e)); return false; } } return false; } @Override protected void onProgressUpdate(Integer... values) { super.onProgressUpdate(values); int downloaded = fileLength / 1014; String size = String.valueOf(values[0] * fileLength/1024/100) + " KB" + String.valueOf(fileLength == 0 ? "" : "/" + downloaded + " KB"); String downloading = context.getResources().getString( R.string.wallpaper_downloading); String text = downloading +"\n"+ size + ""; dialog.setContent(text); } @Override protected void onCancelled() { super.onCancelled(); if (((AppCompatActivity) context).isFinishing()) return; if (file != null) file.delete(); Toast.makeText(context, context.getResources().getString(R.string.wallpaper_download_cancelled), Toast.LENGTH_LONG).show(); } @Override protected void onPostExecute(Boolean aBoolean) { super.onPostExecute(aBoolean); dialog.dismiss(); if (aBoolean) { context.sendBroadcast(new Intent( Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.fromFile( new File(file.toString())))); wallpaperSaved(context, color, file); } else { Toast.makeText(context, context.getResources().getString(R.string.wallpaper_download_failed), Toast.LENGTH_LONG).show(); } } }.execute(); } private static void wallpaperSaved(@Nullable Context context, @ColorInt int color, @NonNull File file) { if (context == null) return; String downloaded = context.getResources().getString( R.string.wallpaper_downloaded); if (Preferences.get(context).getWallsDirectory().length() == 0) Preferences.get(context).setWallsDirectory(file.getParent()); CafeBar.builder(context) .theme(new CafeBarTheme.Custom(ColorHelper.getAttributeColor(context, R.attr.card_background))) .duration(CafeBarDuration.MEDIUM.getDuration()) .fitSystemWindow() .maxLines(4) .typeface("Font-Regular.ttf", "Font-Bold.ttf") .content(downloaded + " " + file.toString()) .icon(R.drawable.ic_toolbar_download) .neutralText(R.string.open) .neutralColor(color) .onNeutral(cafeBar -> { Uri uri = FileHelper.getUriFromFile(context, context.getPackageName(), file); if (uri == null) return; context.startActivity(new Intent() .setAction(Intent.ACTION_VIEW) .setDataAndType(uri, "image/*") .setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)); cafeBar.dismiss(); }) .show(); } private static ImageSize getTargetSize(@NonNull Context context) { Point point = WindowHelper.getScreenSize(context); int targetHeight = point.y; int targetWidth = point.x; if (context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) { targetHeight = point.x; targetWidth = point.y; } if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { int statusBarHeight = WindowHelper.getStatusBarHeight(context); int navBarHeight = WindowHelper.getNavigationBarHeight(context); targetHeight += (statusBarHeight + navBarHeight); } return new ImageSize(targetWidth, targetHeight); } @Nullable private static RectF getScaledRectF(@Nullable RectF rectF, float heightFactor, float widthFactor) { if (rectF == null) return null; RectF scaledRectF = new RectF(rectF); scaledRectF.top *= heightFactor; scaledRectF.bottom *= heightFactor; scaledRectF.left *= widthFactor; scaledRectF.right *= widthFactor; return scaledRectF; } public static void applyWallpaper(@NonNull Context context, @Nullable RectF rectF, @ColorInt int color, String url, String name) { final MaterialDialog.Builder builder = new MaterialDialog.Builder(context); builder.widgetColor(color) .typeface("Font-Medium.ttf", "Font-Regular.ttf") .progress(true, 0) .progressIndeterminateStyle(true) .content(R.string.wallpaper_applying); final MaterialDialog dialog = builder.build(); String imageUri = getWallpaperUri(context, url, name + WallpaperHelper.IMAGE_EXTENSION); ImageSize imageSize = getTargetSize(context); LogUtil.d("target bitmap: " +imageSize.getWidth() +" x "+ imageSize.getHeight()); if (rectF != null && Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT) { Point point = WindowHelper.getScreenSize(context); int height = point.y - WindowHelper.getStatusBarHeight(context) - WindowHelper.getNavigationBarHeight(context); float heightFactor = (float) imageSize.getHeight() / (float) height; rectF = getScaledRectF(rectF, heightFactor, 1f); } prepareBitmap(context, dialog, imageUri, rectF, imageSize); } private static void prepareBitmap(Context context, MaterialDialog dialog, String imageUri, RectF rectF, ImageSize imageSize) { new AsyncTask<Void, Void, Boolean>() { ImageSize adjustedSize = imageSize; RectF adjustedRecF = rectF; @Override protected void onPreExecute() { super.onPreExecute(); if (dialog.isShowing()) return; dialog.setCancelable(false); dialog.setContent(R.string.wallpaper_loading); dialog.show(); } @Override protected Boolean doInBackground(Void... voids) { try { Thread.sleep(1); int height = 0; int width = 0; final BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; if (imageUri.contains("file://")) { BitmapFactory.decodeStream(new FileInputStream(imageUri.replace("file://", "")), null, options); height = options.outHeight; width = options.outWidth; } else { URL url = new URL(imageUri); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); connection.setConnectTimeout(15000); if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) { InputStream stream = connection.getInputStream(); BitmapFactory.decodeStream(stream, null, options); height = options.outHeight; width = options.outWidth; } } LogUtil.d("original bitmap: " + width + " x " + height); if (rectF != null) { LogUtil.d("original recF: " +rectF); } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { float scaleFactor = (float) height / (float) imageSize.getHeight(); if (scaleFactor > 1f) { adjustedSize = new ImageSize(width, height); adjustedRecF = getScaledRectF(rectF, scaleFactor, scaleFactor); LogUtil.d("adjusted bitmap: " + adjustedSize.getWidth() + " x " + adjustedSize.getHeight()); if (adjustedRecF != null) { LogUtil.d("adjusted recF: " + adjustedRecF); } } } return true; } catch (Exception e) { LogUtil.e(Log.getStackTraceString(e)); return false; } } @Override protected void onPostExecute(Boolean aBoolean) { super.onPostExecute(aBoolean); loadBitmap(context, dialog, 1, imageUri, adjustedRecF, adjustedSize); } }.execute(); } private static void loadBitmap(Context context, MaterialDialog dialog, int call, String imageUri, RectF rectF, ImageSize imageSize) { final AsyncTask<Bitmap, Void, Boolean> setWallpaper = getWallpaperAsync( context, dialog, rectF); dialog.setCancelable(true); dialog.setOnDismissListener(dialogInterface -> { ImageLoader.getInstance().stop(); setWallpaper.cancel(true); }); ImageLoader.getInstance().handleSlowNetwork(true); ImageLoader.getInstance().loadImage(imageUri, imageSize, ImageConfig.getWallpaperOptions(), new SimpleImageLoadingListener() { @Override public void onLoadingFailed(String imageUri, View view, FailReason failReason) { if (failReason.getType() == FailReason.FailType.OUT_OF_MEMORY) { if (call <= 5) { double scaleFactor = 1 - (0.1 * call); int scaledWidth = Double.valueOf(imageSize.getWidth() * scaleFactor).intValue(); int scaledHeight = Double.valueOf(imageSize.getHeight() * scaleFactor).intValue(); RectF scaledRecF = getScaledRectF(rectF, (float) scaleFactor, (float) scaleFactor); loadBitmap(context, dialog, (call + 1), imageUri, scaledRecF, new ImageSize(scaledWidth, scaledHeight)); return; } } dialog.dismiss(); String message = context.getResources().getString(R.string.wallpaper_apply_failed); message = message +": "+ failReason.getType().toString(); Toast.makeText(context, message, Toast.LENGTH_LONG).show(); } @Override public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) { LogUtil.d("loaded bitmap: " +loadedImage.getWidth() +" x "+ loadedImage.getHeight()); dialog.setContent(R.string.wallpaper_applying); setWallpaper.execute(loadedImage); } @Override public void onLoadingCancelled(String imageUri, View view) { dialog.dismiss(); Toast.makeText(context, R.string.wallpaper_apply_failed, Toast.LENGTH_LONG).show(); } }); } private static AsyncTask<Bitmap, Void, Boolean> getWallpaperAsync(@NonNull Context context, MaterialDialog dialog, RectF rectF) { return new AsyncTask<Bitmap, Void, Boolean>() { @Override protected Boolean doInBackground(Bitmap... bitmaps) { while (!isCancelled()) { try { Thread.sleep(1); WallpaperManager manager = WallpaperManager.getInstance(context); if (bitmaps[0] != null) { Bitmap bitmap = bitmaps[0]; if (Preferences.get(context).isWallpaperCrop() && rectF != null) { Point point = WindowHelper.getScreenSize(context); int targetWidth = Double.valueOf( ((double) bitmaps[0].getHeight() / (double) point.y) * (double) point.x).intValue(); bitmap = Bitmap.createBitmap( targetWidth, bitmaps[0].getHeight(), bitmaps[0].getConfig()); Paint paint = new Paint(); paint.setFilterBitmap(true); paint.setAntiAlias(true); paint.setDither(true); Canvas canvas = new Canvas(bitmap); canvas.drawBitmap(bitmaps[0], null, rectF, paint); } LogUtil.d("generated bitmap: " +bitmap.getWidth() +" x "+ bitmap.getHeight()); if (Preferences.get(context).isApplyLockscreen() && Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { manager.setBitmap(bitmap, null, true, WallpaperManager.FLAG_LOCK | WallpaperManager.FLAG_SYSTEM); } else { manager.setBitmap(bitmap); } return true; } return false; } catch (Exception | OutOfMemoryError e) { LogUtil.e(Log.getStackTraceString(e)); return false; } } return false; } @Override protected void onCancelled() { super.onCancelled(); Toast.makeText(context, R.string.wallpaper_apply_cancelled, Toast.LENGTH_LONG).show(); } @Override protected void onPostExecute(Boolean aBoolean) { super.onPostExecute(aBoolean); dialog.dismiss(); if (aBoolean) { CafeBar.builder(context) .theme(new CafeBarTheme.Custom(ColorHelper.getAttributeColor( context, R.attr.card_background))) .fitSystemWindow() .content(R.string.wallpaper_applied) .build().show(); } else { Toast.makeText(context, R.string.wallpaper_apply_failed, Toast.LENGTH_LONG).show(); } } }; } }