Skip to content

Commit

Permalink
Merge branch 'master' into jsapi-search-card
Browse files Browse the repository at this point in the history
  • Loading branch information
krmanik authored Aug 5, 2021
2 parents 31ab670 + 2bee386 commit 0ba962d
Show file tree
Hide file tree
Showing 341 changed files with 3,230 additions and 815 deletions.
9 changes: 5 additions & 4 deletions AnkiDroid/build.gradle
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
plugins {
// Gradle plugin portal
id 'com.github.triplet.play' version '3.5.0-agp7.0'
id 'com.github.triplet.play' version '3.6.0'
}

apply plugin: 'com.android.application'
Expand Down Expand Up @@ -52,8 +52,8 @@ android {
//
// This ensures the correct ordering between the various types of releases (dev < alpha < beta < release) which is
// needed for upgrades to be offered correctly.
versionCode=21600107
versionName="2.16alpha7"
versionCode=21600108
versionName="2.16alpha8"
minSdkVersion 21
//noinspection OldTargetApi - also performed in api/build.fradle
targetSdkVersion 29 // change .travis.yml platform download at same time
Expand Down Expand Up @@ -140,7 +140,7 @@ android {

// We want the same version stream for all ABIs in debug but for release we can split them
if (variant.buildType.name == 'release') {
variant.outputs.each { output ->
variant.outputs.all { output ->

// For each separate APK per architecture, set a unique version code as described here:
// https://developer.android.com/studio/build/configure-apk-splits.html
Expand Down Expand Up @@ -309,6 +309,7 @@ dependencies {
implementation 'com.drakeet.drawer:drawer:1.0.3'
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'uk.co.samuelwall:material-tap-target-prompt:3.2.0'
api project(":api")

testImplementation 'org.junit.vintage:junit-vintage-engine:5.7.2'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1705,6 +1705,12 @@ protected WebView createWebView() {
webView.getSettings().setLoadWithOverviewMode(true);
webView.getSettings().setJavaScriptEnabled(true);
webView.setWebChromeClient(new AnkiDroidWebChromeClient());

// This setting toggles file system access within WebView
// The default configuration is already true in apps targeting API <= 29
// Hence, this setting is only useful for apps targeting API >= 30
webView.getSettings().setAllowFileAccess(true);

// Problems with focus and input tags is the reason we keep the old type answer mechanism for old Androids.
webView.setFocusableInTouchMode(mUseInputTag);
webView.setScrollbarFadingEnabled(true);
Expand Down Expand Up @@ -2802,6 +2808,15 @@ public boolean executeCommand(@NonNull ViewerCommand which) {
case COMMAND_TOGGLE_FLAG_BLUE:
toggleFlag(FLAG_BLUE);
return true;
case COMMAND_TOGGLE_FLAG_PINK:
toggleFlag(FLAG_PINK);
return true;
case COMMAND_TOGGLE_FLAG_TURQUOISE:
toggleFlag(FLAG_TURQUOISE);
return true;
case COMMAND_TOGGLE_FLAG_PURPLE:
toggleFlag(FLAG_PURPLE);
return true;
case COMMAND_UNSET_FLAG:
onFlag(mCurrentCard, FLAG_NONE);
return true;
Expand Down Expand Up @@ -4217,7 +4232,7 @@ public void ankiSearchCard(String query) {
startActivityForResultWithAnimation(intent, REQUEST_BROWSE_CARDS, START);
}

@JavascriptInterface

public boolean ankiIsActiveNetworkMetered() {
try {
ConnectivityManager cm = (ConnectivityManager) AnkiDroidApp.getInstance().getApplicationContext()
Expand Down
24 changes: 24 additions & 0 deletions AnkiDroid/src/main/java/com/ichi2/anki/CardBrowser.java
Original file line number Diff line number Diff line change
Expand Up @@ -1199,6 +1199,15 @@ public boolean onOptionsItemSelected(MenuItem item) {
} else if (itemId == R.id.action_flag_four) {
flagTask(4);
return true;
} else if (itemId == R.id.action_flag_five) {
flagTask(5);
return true;
} else if (itemId == R.id.action_flag_six) {
flagTask(6);
return true;
} else if (itemId == R.id.action_flag_seven) {
flagTask(7);
return true;
} else if (itemId == R.id.action_select_flag_zero) {
selectionWithFlagTask(0);
return true;
Expand All @@ -1214,6 +1223,15 @@ public boolean onOptionsItemSelected(MenuItem item) {
} else if (itemId == R.id.action_select_flag_four) {
selectionWithFlagTask(4);
return true;
} else if (itemId == R.id.action_select_flag_five) {
selectionWithFlagTask(5);
return true;
} else if (itemId == R.id.action_select_flag_six) {
selectionWithFlagTask(6);
return true;
} else if (itemId == R.id.action_select_flag_seven) {
selectionWithFlagTask(7);
return true;
} else if (itemId == R.id.action_delete_card) {
deleteSelectedNote();
return true;
Expand Down Expand Up @@ -2582,6 +2600,12 @@ private int getColor() {
return R.attr.flagGreen;
case 4:
return R.attr.flagBlue;
case 5:
return R.attr.flagPink;
case 6:
return R.attr.flagTurquoise;
case 7:
return R.attr.flagPurple;
default:
if (getCard().note().hasTag("marked")) {
return R.attr.markedColor;
Expand Down
2 changes: 1 addition & 1 deletion AnkiDroid/src/main/java/com/ichi2/anki/DeckPicker.java
Original file line number Diff line number Diff line change
Expand Up @@ -571,7 +571,7 @@ void handleStartupFailure(StartupFailure failure) {
break;
case DIRECTORY_NOT_ACCESSIBLE:
Timber.i("AnkiDroid directory inaccessible");
Intent i = Preferences.getPreferenceSubscreenIntent(this, "com.ichi2.anki.prefs.advanced");
Intent i = Preferences.AdvancedSettingsFragment.getSubscreenIntent(this);
startActivityForResultWithoutAnimation(i, REQUEST_PATH_UPDATE);
UIUtils.showThemedToast(this, R.string.directory_inaccessible, false);
break;
Expand Down
93 changes: 77 additions & 16 deletions AnkiDroid/src/main/java/com/ichi2/anki/MediaRegistration.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import com.ichi2.utils.FileUtil;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
Expand All @@ -49,6 +50,11 @@
public class MediaRegistration {

private static final int MEDIA_MAX_SIZE = 5 * 1000 * 1000;
public final static int COLOR_GREY = 0;
public final static int COLOR_TRUE = 2;
public final static int COLOR_INDEX = 3;
public final static int COLOR_GREY_ALPHA = 4;
public final static int COLOR_TRUE_ALPHA = 6;

// Use the same HTML if the same image is pasted multiple times.
private final HashMap<String, String> mPastedImageCache = new HashMap<>();
Expand All @@ -69,7 +75,7 @@ public String loadImageIntoCollection(Uri uri) throws IOException {
String fileName;

String filename = ContentResolverUtil.getFileName(mContext.getContentResolver(), uri);
InputStream fd = mContext.getContentResolver().openInputStream(uri);
InputStream fd = openInputStreamWithURI(uri);

Map.Entry<String, String> fileNameAndExtension = FileUtil.getFileNameAndExtension(filename);

Expand All @@ -82,22 +88,19 @@ public String loadImageIntoCollection(Uri uri) throws IOException {
File clipCopy;
long bytesWritten;

if (!".png".equals(fileNameAndExtension.getValue()) && CollectionHelper.getInstance().getCol(mContext).getConf().optBoolean("pastePNG")) {
clipCopy = File.createTempFile(fileName, ".png");
bytesWritten = CompatHelper.getCompat().copyFile(fd, clipCopy.getAbsolutePath());
Bitmap bm = BitmapFactory.decodeFile(clipCopy.getAbsolutePath());
try (FileOutputStream outStream = new FileOutputStream(clipCopy.getAbsolutePath())) {
bm.compress(Bitmap.CompressFormat.PNG, 100, outStream);
outStream.flush();
} catch (IOException e) {
Timber.w("MediaRegistration : Unable to convert file to png format");
AnkiDroidApp.sendExceptionReport(e, "Unable to convert file to png format");;
UIUtils.showThemedToast(mContext, mContext.getResources().getString(R.string.multimedia_editor_png_paste_error, e.getMessage()), true);
return null;
try (InputStream copyFd = openInputStreamWithURI(uri)) {
// no conversion to jpg in cases of gif and jpg and if png image with alpha channel
if (shouldConvertToJPG(fileNameAndExtension.getValue(), copyFd)) {
clipCopy = File.createTempFile(fileName, ".jpg");
bytesWritten = CompatHelper.getCompat().copyFile(fd, clipCopy.getAbsolutePath());
// return null if jpg conversion false.
if (!convertToJPG(clipCopy)) {
return null;
}
} else {
clipCopy = File.createTempFile(fileName, fileNameAndExtension.getValue());
bytesWritten = CompatHelper.getCompat().copyFile(fd, clipCopy.getAbsolutePath());
}
} else {
clipCopy = File.createTempFile(fileName, fileNameAndExtension.getValue());
bytesWritten = CompatHelper.getCompat().copyFile(fd, clipCopy.getAbsolutePath());
}

String tempFilePath = clipCopy.getAbsolutePath();
Expand All @@ -121,6 +124,64 @@ public String loadImageIntoCollection(Uri uri) throws IOException {
return field.getFormattedValue();
}


private InputStream openInputStreamWithURI(Uri uri) throws FileNotFoundException {
return mContext.getContentResolver().openInputStream(uri);
}

private boolean convertToJPG(File file) {
Bitmap bm = BitmapFactory.decodeFile(file.getAbsolutePath());
try (FileOutputStream outStream = new FileOutputStream(file.getAbsolutePath())) {
bm.compress(Bitmap.CompressFormat.JPEG, 100, outStream);
outStream.flush();
} catch (IOException e) {
Timber.w("MediaRegistration : Unable to convert file to png format");
AnkiDroidApp.sendExceptionReport(e, "Unable to convert file to png format");;
UIUtils.showThemedToast(mContext, mContext.getResources().getString(R.string.multimedia_editor_png_paste_error, e.getMessage()), true);
return false;
}
return true; // successful conversion to jpg.
}

private boolean shouldConvertToJPG(String fileNameExtension, InputStream fileStream) {
if (".jpg".equals(fileNameExtension)) {
return false; // we are already a jpg, no conversion
}
if (".gif".equals(fileNameExtension)) {
return false; // gifs may have animation, conversion would ruin them
}
if ((".png".equals(fileNameExtension) && doesInputStreamContainTransparency(fileStream))) {
return false; // pngs with transparency would be ruined by conversion
}
return true;
}

/**
* given an inputStream of a file,
* returns true if found that it has transparency (in its header)
* code: https://stackoverflow.com/a/31311718/14148406
*/
private static boolean doesInputStreamContainTransparency(InputStream inputStream) {
try {
// skip: png signature,header chunk declaration,width,height,bitDepth :
inputStream.skip(12 + 4 + 4 + 4 + 1);
final byte colorType = (byte) inputStream.read();
switch (colorType) {
case COLOR_GREY_ALPHA:
case COLOR_TRUE_ALPHA:
return true;
case COLOR_INDEX:
case COLOR_GREY:
case COLOR_TRUE:
return false;
}
return true;
} catch (final Exception e) {
Timber.w(e, "Failed to check transparency of inputStream");
}
return false;
}

public boolean checkFilename(Map.Entry<String, String> fileNameAndExtension) {
return fileNameAndExtension.getKey().length() <= 3;
}
Expand Down
Loading

0 comments on commit 0ba962d

Please sign in to comment.