/*
* Copyright (C) 2015 The Android Open Source 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.android.talkback.labeling;
import android.content.Context;
import android.net.Uri;
import android.os.Handler;
import android.support.annotation.NonNull;
import android.text.TextUtils;
import android.util.Log;
import android.widget.Toast;
import com.android.talkback.R;
import com.android.utils.LogUtils;
import com.android.utils.labeling.CustomLabelManager;
import com.android.utils.labeling.Label;
import com.google.android.marvin.talkback.TalkBackService;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Writer;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class CustomLabelMigrationManager {
private static final String JSON_LABELS_ARRAY = "labels_array";
private static final String JSON_LABEL_PACKAGE_NAME = "package_name";
private static final String JSON_LABEL_PACKAGE_SIGNATURE = "package_signature";
private static final String JSON_LABEL_VIEW_NAME = "view_name";
private static final String JSON_LABEL_TEXT = "label_text";
private static final String JSON_LABEL_LOCALE = "locale";
private static final String JSON_PACKAGE_VERSION = "package_version";
private static final String JSON_TIMESTAMP = "timestamp";
public interface OnLabelMigrationCallback {
public void onLabelsExported(File file);
public void onLabelImported(int updateCount);
public void onFail();
}
public static class SimpleLabelMigrationCallback implements OnLabelMigrationCallback {
@Override
public void onLabelsExported(File file) { }
@Override
public void onLabelImported(int updateCount) { }
@Override
public void onFail() { }
}
private ExecutorService mExecutor;
private Handler mHandler;
private CustomLabelManager mManager;
private Context mContext;
public CustomLabelMigrationManager(Context context) {
mExecutor = Executors.newSingleThreadExecutor();
mHandler = new Handler();
TalkBackService service = TalkBackService.getInstance();
if (service != null) {
mManager = service.getLabelManager();
} else {
mManager = new CustomLabelManager(context);
}
mContext = context;
}
public void exportLabels(final OnLabelMigrationCallback callback) {
mManager.getLabelsFromDatabase(new LabelsFetchRequest.OnLabelsFetchedListener() {
@Override
public void onLabelsFetched(List<Label> results) {
if (results != null && results.size() > 0) {
createLabelFileAsync(results, callback);
} else {
Toast.makeText(mContext, R.string.label_export_empty, Toast.LENGTH_SHORT)
.show();
}
}
});
}
private void createLabelFileAsync(final List<Label> labels,
final OnLabelMigrationCallback callback) {
mExecutor.execute(new Runnable() {
@Override
public void run() {
try {
String jsonText = generateJsonText(labels);
if (jsonText != null) {
final File file = getFilePath();
writeToFile(jsonText, file);
if (callback != null) {
mHandler.post(new Runnable() {
@Override
public void run() {
callback.onLabelsExported(file);
}
});
}
} else {
notifyFailure(callback);
}
} catch (Exception e) {
notifyFailure(callback);
LogUtils.log(Log.ERROR, "Failed to export labels");
}
}
});
}
private void notifyFailure(final OnLabelMigrationCallback callback) {
if (callback != null) {
mHandler.post(new Runnable() {
@Override
public void run() {
callback.onFail();
}
});
}
}
// reduced visibility for tests
String generateJsonText(List<Label> labels) throws JSONException {
JSONObject root = new JSONObject();
JSONArray labelsArray = new JSONArray();
for (Label label : labels) {
if (label != null) {
JSONObject labelObject = new JSONObject();
labelObject.put(JSON_LABEL_PACKAGE_NAME, label.getPackageName());
labelObject.put(JSON_LABEL_PACKAGE_SIGNATURE, label.getPackageSignature());
labelObject.put(JSON_LABEL_VIEW_NAME, label.getViewName());
labelObject.put(JSON_LABEL_TEXT, label.getText());
labelObject.put(JSON_LABEL_LOCALE, label.getLocale());
labelObject.put(JSON_PACKAGE_VERSION, label.getPackageVersion());
labelObject.put(JSON_TIMESTAMP, label.getTimestamp());
labelsArray.put(labelObject);
}
}
root.put(JSON_LABELS_ARRAY, labelsArray);
return root.toString();
}
private File getFilePath() throws IOException {
File outputDir = mContext.getExternalCacheDir();
String fileName = String.format("Talkback_custom_labels_%s.tbl",
new SimpleDateFormat("MMddyyyy").format(new Date()));
return new File(outputDir, fileName);
}
private void writeToFile(String content, File file) throws IOException {
Writer writer = new BufferedWriter(new FileWriter(file));
writer.write(content);
writer.close();
}
public void importLabels(Uri contentUri, boolean overrideExistingLabels,
OnLabelMigrationCallback callback) {
try {
String text = readText(contentUri);
if (text == null) {
return;
}
List<Label> labels = parseLabels(text);
if (labels.size() == 0) {
return;
}
mManager.importLabels(labels, overrideExistingLabels, callback);
} catch (Exception e) {
notifyFailure(callback);
LogUtils.log(Log.ERROR, "failed to import labels");
}
}
private String readText(Uri contentUri) throws IOException {
StringBuilder text = new StringBuilder();
InputStream is = mContext.getContentResolver().openInputStream(contentUri);
BufferedReader br = new BufferedReader(new InputStreamReader(is));
String line;
while ((line = br.readLine()) != null) {
text.append(line);
text.append('\n');
}
br.close();
return text.toString();
}
// reduced visibility for tests
@NonNull List<Label> parseLabels(String jsonText) throws JSONException {
JSONObject root = new JSONObject(jsonText);
JSONArray labelsArray = root.getJSONArray(JSON_LABELS_ARRAY);
if (labelsArray == null) {
return Collections.emptyList();
}
int count = labelsArray.length();
List<Label> result = new ArrayList<>(count);
for (int i = 0; i < count; i++) {
JSONObject labelObject = labelsArray.getJSONObject(i);
String packageName = labelObject.getString(JSON_LABEL_PACKAGE_NAME);
if (TextUtils.isEmpty(packageName)) {
continue;
}
String packageSignature = labelObject.getString(JSON_LABEL_PACKAGE_SIGNATURE);
String viewName = labelObject.getString(JSON_LABEL_VIEW_NAME);
if (TextUtils.isEmpty(viewName)) {
continue;
}
String labelText = labelObject.getString(JSON_LABEL_TEXT);
if (TextUtils.isEmpty(labelText)) {
continue;
}
String locale = labelObject.getString(JSON_LABEL_LOCALE);
int packageVersion = labelObject.getInt(JSON_PACKAGE_VERSION);
long timestamp = labelObject.getLong(JSON_TIMESTAMP);
Label label = new Label(packageName, packageSignature, viewName, labelText, locale,
packageVersion, "", timestamp);
result.add(label);
}
return result;
}
}