/*
* Copyright (C) 2017 The Android Open Kang Project
*
* 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.
*/
package com.aokp.romcontrol.fragments.general;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.AlertDialog;
import android.app.Fragment;
import android.content.ContentResolver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.Resources;
import android.graphics.BitmapFactory;
import android.graphics.drawable.AnimationDrawable;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.Point;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.os.SystemProperties;
import android.preference.ListPreference;
import android.preference.Preference;
import android.preference.Preference.OnPreferenceChangeListener;
import android.preference.PreferenceFragment;
import android.preference.PreferenceScreen;
import android.provider.Settings;
import android.support.design.widget.Snackbar;
import android.util.Log;
import android.view.Display;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.channels.FileChannel;
import java.security.SecureRandom;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Enumeration;
import java.util.List;
import java.util.Random;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import com.aokp.romcontrol.R;
import com.aokp.romcontrol.util.Helpers;
import com.aokp.romcontrol.util.AbstractAsyncSuCMDProcessor;
import com.aokp.romcontrol.util.CMDProcessor;
import com.aokp.romcontrol.widgets.SeekBarPreferenceCham;
public class GeneralSettingsFragment extends Fragment {
public GeneralSettingsFragment() {
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_general_settings_main, container, false);
Resources res = getResources();
super.onCreate(savedInstanceState);
getChildFragmentManager().beginTransaction()
.replace(R.id.general_settings_main, new GeneralSettingsPreferenceFragment())
.commit();
return v;
}
public static class GeneralSettingsPreferenceFragment extends PreferenceFragment
implements Preference.OnPreferenceChangeListener {
public GeneralSettingsPreferenceFragment() {
}
private static final String TAG = "GeneralSettingsPreferenceFragment";
private static final int REQUEST_PICK_BOOT_ANIMATION = 201;
private static final String KEY_LOCKCLOCK = "lock_clock";
// Package name of the cLock app
public static final String LOCKCLOCK_PACKAGE_NAME = "com.cyanogenmod.lockclock";
private static final String SCREENSHOT_TYPE = "screenshot_type";
private static final String SCREENSHOT_DELAY = "screenshot_delay";
private static final String PREF_MEDIA_SCANNER_ON_BOOT = "media_scanner_on_boot";
private static final String PREF_CUSTOM_BOOTANIM = "custom_bootanimation";
private static final String BOOTANIMATION_SYSTEM_PATH = "/system/media/bootanimation.zip";
private static final String BACKUP_PATH = new File(Environment
.getExternalStorageDirectory(), "/AOKP_ota").getAbsolutePath();
private Context mContext;
private Preference mLockClock;
private ListPreference mScreenshotType;
private SeekBarPreferenceCham mScreenshotDelay;
private Preference mCustomBootAnimation;
private ImageView mView;
private TextView mError;
private AlertDialog mCustomBootAnimationDialog;
private AnimationDrawable mAnimationPart1;
private AnimationDrawable mAnimationPart2;
private String mErrormsg;
private String mBootAnimationPath;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
createCustomView();
}
private PreferenceScreen createCustomView() {
// Load the preferences from an XML resource
addPreferencesFromResource(R.xml.fragment_general_settings);
PreferenceScreen prefSet = getPreferenceScreen();
mContext = getActivity().getApplicationContext();
ContentResolver resolver = getActivity().getContentResolver();
PackageManager pm = getActivity().getPackageManager();
// cLock app check
mLockClock = (Preference)
prefSet.findPreference(KEY_LOCKCLOCK);
if (!Helpers.isPackageInstalled(LOCKCLOCK_PACKAGE_NAME, pm)) {
prefSet.removePreference(mLockClock);
}
mScreenshotType = (ListPreference) findPreference(SCREENSHOT_TYPE);
int mScreenshotTypeValue = Settings.System.getInt(resolver,
Settings.System.SCREENSHOT_TYPE, 0);
mScreenshotType.setValue(String.valueOf(mScreenshotTypeValue));
mScreenshotType.setSummary(mScreenshotType.getEntry());
mScreenshotType.setOnPreferenceChangeListener(this);
mScreenshotDelay = (SeekBarPreferenceCham) findPreference(SCREENSHOT_DELAY);
int screenshotDelay = Settings.System.getInt(resolver,
Settings.System.SCREENSHOT_DELAY, 100);
mScreenshotDelay.setValue(screenshotDelay / 1);
mScreenshotDelay.setOnPreferenceChangeListener(this);
// Custom bootanimation
mCustomBootAnimation = findPreference(PREF_CUSTOM_BOOTANIM);
resetBootAnimation();
return prefSet;
}
@Override
public void onPause() {
super.onPause();
}
@Override
public void onResume() {
super.onResume();
}
@Override
public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
if (preference == mCustomBootAnimation) {
openBootAnimationDialog();
} else {
return super.onPreferenceTreeClick(preferenceScreen, preference);
}
return true;
}
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
ContentResolver resolver = getActivity().getContentResolver();
if (preference == mScreenshotType) {
int mScreenshotTypeValue = Integer.parseInt(((String) newValue).toString());
mScreenshotType.setSummary(
mScreenshotType.getEntries()[mScreenshotTypeValue]);
Settings.System.putInt(resolver,
Settings.System.SCREENSHOT_TYPE, mScreenshotTypeValue);
mScreenshotType.setValue(String.valueOf(mScreenshotTypeValue));
return true;
} else if (preference == mScreenshotDelay) {
int screenshotDelay = (Integer) newValue;
Settings.System.putInt(resolver,
Settings.System.SCREENSHOT_DELAY, screenshotDelay * 1);
return true;
}
return false;
}
/**
* Resets boot animation path. Essentially clears temporary-set boot animation
* set by the user from the dialog.
*
* @return returns true if a boot animation exists (user or system). false otherwise.
*/
private boolean resetBootAnimation() {
boolean bootAnimationExists = false;
if (new File(BOOTANIMATION_SYSTEM_PATH).exists()) {
mBootAnimationPath = BOOTANIMATION_SYSTEM_PATH;
bootAnimationExists = true;
} else {
mBootAnimationPath = "";
}
return bootAnimationExists;
}
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode == Activity.RESULT_OK) {
if (requestCode == REQUEST_PICK_BOOT_ANIMATION) {
if (data == null) {
//Nothing returned by user, probably pressed back button in file manager
return;
}
mBootAnimationPath = data.getData().getPath();
openBootAnimationDialog();
}
}
}
private void openBootAnimationDialog() {
Log.e(TAG, "boot animation path: " + mBootAnimationPath);
if (mCustomBootAnimationDialog != null) {
mCustomBootAnimationDialog.cancel();
mCustomBootAnimationDialog = null;
}
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setTitle(R.string.bootanimation_preview);
if (!mBootAnimationPath.isEmpty()
&& (!BOOTANIMATION_SYSTEM_PATH.equalsIgnoreCase(mBootAnimationPath))) {
builder.setPositiveButton(R.string.apply, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
installBootAnim(dialog, mBootAnimationPath);
resetBootAnimation();
}
});
}
builder.setNeutralButton(R.string.set_custom_bootanimation,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
PackageManager packageManager = getActivity().getPackageManager();
Intent test = new Intent(Intent.ACTION_GET_CONTENT);
test.setType("file/*");
List<ResolveInfo> list = packageManager.queryIntentActivities(test,
PackageManager.GET_ACTIVITIES);
if (!list.isEmpty()) {
Intent intent = new Intent(Intent.ACTION_GET_CONTENT, null);
intent.setType("file/*");
startActivityForResult(intent, REQUEST_PICK_BOOT_ANIMATION);
} else {
//No app installed to handle the intent - file explorer required
Snackbar.make(getView(), R.string.install_file_manager_error,
Snackbar.LENGTH_SHORT).show();
}
}
});
builder.setNegativeButton(com.android.internal.R.string.cancel,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
resetBootAnimation();
dialog.dismiss();
}
});
LayoutInflater inflater =
(LayoutInflater) getActivity()
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View layout = inflater.inflate(R.layout.dialog_bootanimation_preview,
(ViewGroup) getActivity()
.findViewById(R.id.bootanimation_layout_root));
mError = (TextView) layout.findViewById(R.id.textViewError);
mView = (ImageView) layout.findViewById(R.id.imageViewPreview);
mView.setVisibility(View.GONE);
Display display = getActivity().getWindowManager().getDefaultDisplay();
Point size = new Point();
display.getSize(size);
mView.setLayoutParams(new LinearLayout.LayoutParams(size.x / 2, size.y / 2));
mError.setText(R.string.creating_preview);
builder.setView(layout);
mCustomBootAnimationDialog = builder.create();
mCustomBootAnimationDialog.setOwnerActivity(getActivity());
mCustomBootAnimationDialog.show();
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
createPreview(mBootAnimationPath);
}
});
thread.start();
}
private void createPreview(String path) {
File zip = new File(path);
ZipFile zipfile = null;
String desc = "";
InputStream inputStream = null;
InputStreamReader inputStreamReader = null;
BufferedReader bufferedReader = null;
try {
zipfile = new ZipFile(zip);
ZipEntry ze = zipfile.getEntry("desc.txt");
inputStream = zipfile.getInputStream(ze);
inputStreamReader = new InputStreamReader(inputStream);
StringBuilder sb = new StringBuilder(0);
bufferedReader = new BufferedReader(inputStreamReader);
String read = bufferedReader.readLine();
while (read != null) {
sb.append(read);
sb.append('\n');
read = bufferedReader.readLine();
}
desc = sb.toString();
} catch (Exception handleAllException) {
mErrormsg = getActivity().getString(R.string.error_reading_zip_file);
errorHandler.sendEmptyMessage(0);
return;
} finally {
try {
if (bufferedReader != null) {
bufferedReader.close();
}
} catch (IOException e) {
// we tried
}
try {
if (inputStreamReader != null) {
inputStreamReader.close();
}
} catch (IOException e) {
// we tried
}
try {
if (inputStream != null) {
inputStream.close();
}
} catch (IOException e) {
// moving on...
}
}
String[] info = desc.replace("\\r", "").split("\\n");
// ignore first two ints height and width
int delay = Integer.parseInt(info[0].split(" ")[2]);
String partName1 = info[1].split(" ")[3];
String partName2;
try {
if (info.length > 2) {
partName2 = info[2].split(" ")[3];
} else {
partName2 = "";
}
} catch (Exception e) {
partName2 = "";
}
BitmapFactory.Options opt = new BitmapFactory.Options();
opt.inSampleSize = 4;
mAnimationPart1 = new AnimationDrawable();
mAnimationPart2 = new AnimationDrawable();
try {
for (Enumeration<? extends ZipEntry> enumeration = zipfile.entries();
enumeration.hasMoreElements(); ) {
ZipEntry entry = enumeration.nextElement();
if (entry.isDirectory()) {
continue;
}
String partname = entry.getName().split("/")[0];
if (partName1.equalsIgnoreCase(partname)) {
InputStream partOneInStream = null;
try {
partOneInStream = zipfile.getInputStream(entry);
mAnimationPart1.addFrame(new BitmapDrawable(getResources(),
BitmapFactory.decodeStream(partOneInStream,
null, opt)), delay);
} finally {
if (partOneInStream != null) {
partOneInStream.close();
}
}
} else if (partName2.equalsIgnoreCase(partname)) {
InputStream partTwoInStream = null;
try {
partTwoInStream = zipfile.getInputStream(entry);
mAnimationPart2.addFrame(new BitmapDrawable(getResources(),
BitmapFactory.decodeStream(partTwoInStream,
null, opt)), delay);
} finally {
if (partTwoInStream != null) {
partTwoInStream.close();
}
}
}
}
} catch (IOException e1) {
mErrormsg = getActivity().getString(R.string.error_creating_preview);
errorHandler.sendEmptyMessage(0);
return;
}
if (!partName2.isEmpty()) {
Log.d(TAG, "Multipart Animation");
mAnimationPart1.setOneShot(false);
mAnimationPart2.setOneShot(false);
mAnimationPart1.setOnAnimationFinishedListener(
new AnimationDrawable.OnAnimationFinishedListener() {
@Override
public void onAnimationFinished() {
Log.d(TAG, "First part finished");
mView.setImageDrawable(mAnimationPart2);
mAnimationPart1.stop();
mAnimationPart2.start();
}
});
} else {
mAnimationPart1.setOneShot(false);
}
finishedHandler.sendEmptyMessage(0);
}
private Handler errorHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
mView.setVisibility(View.GONE);
mError.setText(mErrormsg);
}
};
private Handler finishedHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
mView.setImageDrawable(mAnimationPart1);
mView.setVisibility(View.VISIBLE);
mError.setVisibility(View.GONE);
mAnimationPart1.start();
}
};
private void installBootAnim(DialogInterface dialog, String bootAnimationPath) {
DateFormat dateFormat = new SimpleDateFormat("ddMMyyyy_HHmmss");
Date date = new Date();
String current = (dateFormat.format(date));
new AbstractAsyncSuCMDProcessor() {
@Override
protected void onPostExecute(String result) {
}
}.execute("mount -o rw,remount /system",
"cp -f /system/media/bootanimation.zip " + BACKUP_PATH + "/bootanimation_backup_" + current + ".zip",
"cp -f " + bootAnimationPath + " /system/media/bootanimation.zip",
"chmod 644 /system/media/bootanimation.zip",
"mount -o ro,remount /system");
}
}
}