mirror of
git://f0xx.org/android_cast
synced 2026-07-29 04:18:09 +03:00
67
Dockerfile
Normal file
67
Dockerfile
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
# Minimal Android Cast CI image (glibc-based; NDK prebuilts require linux-x86_64 glibc).
|
||||||
|
#
|
||||||
|
# Build:
|
||||||
|
# docker build -t androidcast-ci:latest -f Dockerfile .
|
||||||
|
#
|
||||||
|
# Run CI build (mount repo):
|
||||||
|
# docker run --rm -v "$(pwd)":/workspace -w /workspace androidcast-ci:latest
|
||||||
|
#
|
||||||
|
# Interactive:
|
||||||
|
# docker run --rm -it -v "$(pwd)":/workspace -w /workspace androidcast-ci:latest bash
|
||||||
|
#
|
||||||
|
# Note: Alpine/musl breaks NDK clang prebuilts; use Debian slim instead of rust:* for size.
|
||||||
|
|
||||||
|
FROM debian:bookworm-slim
|
||||||
|
|
||||||
|
ARG DEBIAN_FRONTEND=noninteractive
|
||||||
|
ARG ANDROID_SDK_ROOT=/opt/android-sdk
|
||||||
|
ARG ANDROID_NDK_VERSION=27.0.12077973
|
||||||
|
ARG ANDROID_CMAKE_VERSION=3.22.1
|
||||||
|
ARG CMDLINE_TOOLS_REV=11076708
|
||||||
|
|
||||||
|
ENV ANDROID_SDK_ROOT="${ANDROID_SDK_ROOT}" \
|
||||||
|
ANDROID_HOME="${ANDROID_SDK_ROOT}" \
|
||||||
|
ANDROID_NDK_HOME="${ANDROID_SDK_ROOT}/ndk/${ANDROID_NDK_VERSION}" \
|
||||||
|
ANDROID_NDK_ROOT="${ANDROID_SDK_ROOT}/ndk/${ANDROID_NDK_VERSION}" \
|
||||||
|
PATH="${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin:${PATH}" \
|
||||||
|
ANDROIDCAST_CCACHE=1 \
|
||||||
|
ANDROIDCAST_USE_DISTCC=0 \
|
||||||
|
GRADLE_USER_HOME=/root/.gradle \
|
||||||
|
JAVA_HOME=/usr/lib/jvm/java-21-openjdk-amd64
|
||||||
|
|
||||||
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
|
bash ca-certificates curl git openjdk-21-jdk-headless unzip wget \
|
||||||
|
ninja-build cmake ccache \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# Optional distributed compile (off unless ANDROIDCAST_USE_DISTCC=1 + DISTCC_HOSTS at run time).
|
||||||
|
RUN apt-get update && apt-get install -y --no-install-recommends distcc \
|
||||||
|
&& rm -rf /var/lib/apt/lists/* || true
|
||||||
|
|
||||||
|
RUN mkdir -p "${ANDROID_SDK_ROOT}/cmdline-tools" \
|
||||||
|
&& curl -fsSL "https://dl.google.com/android/repository/commandlinetools-linux-${CMDLINE_TOOLS_REV}_latest.zip" \
|
||||||
|
-o /tmp/cmdline-tools.zip \
|
||||||
|
&& unzip -q /tmp/cmdline-tools.zip -d "${ANDROID_SDK_ROOT}/cmdline-tools" \
|
||||||
|
&& mv "${ANDROID_SDK_ROOT}/cmdline-tools/cmdline-tools" "${ANDROID_SDK_ROOT}/cmdline-tools/latest" \
|
||||||
|
&& rm -f /tmp/cmdline-tools.zip
|
||||||
|
|
||||||
|
RUN yes | sdkmanager --licenses >/dev/null \
|
||||||
|
&& sdkmanager \
|
||||||
|
"platform-tools" \
|
||||||
|
"platforms;android-34" \
|
||||||
|
"build-tools;34.0.0" \
|
||||||
|
"ndk;${ANDROID_NDK_VERSION}" \
|
||||||
|
"cmake;${ANDROID_CMAKE_VERSION}"
|
||||||
|
|
||||||
|
WORKDIR /workspace
|
||||||
|
|
||||||
|
# Pre-create ccache dir (volume can override at runtime).
|
||||||
|
RUN mkdir -p /workspace/build/.ccache || true
|
||||||
|
|
||||||
|
COPY scripts/ci-build.sh scripts/android-ndk.sh scripts/native-build-cache.sh scripts/build-native-codecs.sh \
|
||||||
|
/workspace/scripts/
|
||||||
|
|
||||||
|
RUN chmod +x /workspace/scripts/ci-build.sh /workspace/scripts/build-native-codecs.sh
|
||||||
|
|
||||||
|
# Full tree is bind-mounted in CI; default command runs the pipeline.
|
||||||
|
CMD ["./scripts/ci-build.sh"]
|
||||||
@@ -2,6 +2,24 @@ plugins {
|
|||||||
id 'com.android.application'
|
id 'com.android.application'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def resolveCcachePath = {
|
||||||
|
def fromEnv = System.getenv('CCACHE') ?: System.getenv('NDK_CCACHE')
|
||||||
|
if (fromEnv) {
|
||||||
|
return fromEnv
|
||||||
|
}
|
||||||
|
if (System.getenv('ANDROIDCAST_CCACHE') in ['0', 'false', 'no', 'off']) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
def out = providers.exec {
|
||||||
|
commandLine 'sh', '-c', 'command -v ccache || true'
|
||||||
|
}.standardOutput.asText.get().trim()
|
||||||
|
return out ?: null
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
def gitCommitShort = 'unknown'
|
def gitCommitShort = 'unknown'
|
||||||
def gitCommitAuthor = 'unknown'
|
def gitCommitAuthor = 'unknown'
|
||||||
def buildTimeEpochMs = System.currentTimeMillis()
|
def buildTimeEpochMs = System.currentTimeMillis()
|
||||||
@@ -31,7 +49,11 @@ android {
|
|||||||
buildConfigField 'String', 'BUILD_TIME_DISPLAY', "\"${buildTimeDisplay.replace('\\\\', '\\\\\\\\').replace('\"', '\\\\\"')}\""
|
buildConfigField 'String', 'BUILD_TIME_DISPLAY', "\"${buildTimeDisplay.replace('\\\\', '\\\\\\\\').replace('\"', '\\\\\"')}\""
|
||||||
externalNativeBuild {
|
externalNativeBuild {
|
||||||
cmake {
|
cmake {
|
||||||
arguments "-DANDROID_STL=c++_shared"
|
def cmakeArgs = ["-DANDROID_STL=c++_shared"]
|
||||||
|
if (resolveCcachePath) {
|
||||||
|
cmakeArgs += "-DANDROID_CCACHE=${resolveCcachePath}"
|
||||||
|
}
|
||||||
|
arguments cmakeArgs
|
||||||
cppFlags ""
|
cppFlags ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -115,14 +115,8 @@ public final class AppPreferences {
|
|||||||
prefs(context).edit().putBoolean(KEY_DEV_GRAB_SESSION_STATS, grab).apply();
|
prefs(context).edit().putBoolean(KEY_DEV_GRAB_SESSION_STATS, grab).apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Developer: show priority scores in codec settings spinners (default off). */
|
||||||
* Show priority score suffix in codec settings spinners (also on when receiver debug overlay is on).
|
|
||||||
*/
|
|
||||||
public static boolean isShowCodecPriorityScores(Context context) {
|
public static boolean isShowCodecPriorityScores(Context context) {
|
||||||
return isShowReceiverDebugOverlay(context) || isDevShowCodecPriorityScoresExplicit(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean isDevShowCodecPriorityScoresExplicit(Context context) {
|
|
||||||
return prefs(context).getBoolean(KEY_DEV_SHOW_CODEC_PRIORITY_SCORES, false);
|
return prefs(context).getBoolean(KEY_DEV_SHOW_CODEC_PRIORITY_SCORES, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -180,10 +180,13 @@ public final class CastSettingsBinder {
|
|||||||
List<CodecCatalog.VideoCodecChoice> choices = CodecCatalog.videoCodecChoicesForUi(settings);
|
List<CodecCatalog.VideoCodecChoice> choices = CodecCatalog.videoCodecChoicesForUi(settings);
|
||||||
CastSettings.VideoCodec[] values = new CastSettings.VideoCodec[choices.size()];
|
CastSettings.VideoCodec[] values = new CastSettings.VideoCodec[choices.size()];
|
||||||
String[] labels = new String[choices.size()];
|
String[] labels = new String[choices.size()];
|
||||||
|
int[] scores = new int[choices.size()];
|
||||||
boolean[] selectable = new boolean[choices.size()];
|
boolean[] selectable = new boolean[choices.size()];
|
||||||
|
boolean showScores = AppPreferences.isShowCodecPriorityScores(activity);
|
||||||
for (int i = 0; i < choices.size(); i++) {
|
for (int i = 0; i < choices.size(); i++) {
|
||||||
values[i] = choices.get(i).codec;
|
values[i] = choices.get(i).codec;
|
||||||
labels[i] = labelVideoCodec(activity, choices.get(i).codec, choices.get(i).priorityScore);
|
labels[i] = labelVideoCodec(activity, choices.get(i).codec);
|
||||||
|
scores[i] = codecSpinnerScore(showScores, choices.get(i).codec, choices.get(i).priorityScore);
|
||||||
selectable[i] = choices.get(i).selectable;
|
selectable[i] = choices.get(i).selectable;
|
||||||
}
|
}
|
||||||
CastSettings.VideoCodec current = settings.getVideoCodec();
|
CastSettings.VideoCodec current = settings.getVideoCodec();
|
||||||
@@ -191,7 +194,7 @@ public final class CastSettingsBinder {
|
|||||||
current = CastSettings.VideoCodec.AUTO;
|
current = CastSettings.VideoCodec.AUTO;
|
||||||
settings.setVideoCodec(current);
|
settings.setVideoCodec(current);
|
||||||
}
|
}
|
||||||
SettingsEnumSpinner.bind(spinner, values, labels, selectable, current, settings::setVideoCodec);
|
SettingsEnumSpinner.bind(spinner, values, labels, scores, selectable, current, settings::setVideoCodec);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Rebind codec spinners after {@code codecs.json} reload or debug score toggle. */
|
/** Rebind codec spinners after {@code codecs.json} reload or debug score toggle. */
|
||||||
@@ -226,10 +229,13 @@ public final class CastSettingsBinder {
|
|||||||
List<CodecCatalog.AudioCodecChoice> choices = CodecCatalog.audioCodecChoicesForUi(settings);
|
List<CodecCatalog.AudioCodecChoice> choices = CodecCatalog.audioCodecChoicesForUi(settings);
|
||||||
CastSettings.AudioCodec[] values = new CastSettings.AudioCodec[choices.size()];
|
CastSettings.AudioCodec[] values = new CastSettings.AudioCodec[choices.size()];
|
||||||
String[] labels = new String[choices.size()];
|
String[] labels = new String[choices.size()];
|
||||||
|
int[] scores = new int[choices.size()];
|
||||||
boolean[] selectable = new boolean[choices.size()];
|
boolean[] selectable = new boolean[choices.size()];
|
||||||
|
boolean showScores = AppPreferences.isShowCodecPriorityScores(activity);
|
||||||
for (int i = 0; i < choices.size(); i++) {
|
for (int i = 0; i < choices.size(); i++) {
|
||||||
values[i] = choices.get(i).codec;
|
values[i] = choices.get(i).codec;
|
||||||
labels[i] = labelAudioCodec(activity, choices.get(i).codec, choices.get(i).priorityScore);
|
labels[i] = labelAudioCodec(activity, choices.get(i).codec);
|
||||||
|
scores[i] = codecSpinnerScore(showScores, choices.get(i).codec, choices.get(i).priorityScore);
|
||||||
selectable[i] = choices.get(i).selectable;
|
selectable[i] = choices.get(i).selectable;
|
||||||
}
|
}
|
||||||
CastSettings.AudioCodec current = settings.getAudioCodec();
|
CastSettings.AudioCodec current = settings.getAudioCodec();
|
||||||
@@ -240,7 +246,7 @@ public final class CastSettingsBinder {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SettingsEnumSpinner.bind(spinner, values, labels, selectable, current, settings::setAudioCodec);
|
SettingsEnumSpinner.bind(spinner, values, labels, scores, selectable, current, settings::setAudioCodec);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void readAudioCodec(AppCompatActivity activity, CastSettings settings) {
|
private static void readAudioCodec(AppCompatActivity activity, CastSettings settings) {
|
||||||
@@ -560,57 +566,46 @@ public final class CastSettingsBinder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String labelVideoCodec(AppCompatActivity activity, CastSettings.VideoCodec codec,
|
private static String labelVideoCodec(AppCompatActivity activity, CastSettings.VideoCodec codec) {
|
||||||
int priorityScore) {
|
|
||||||
String base;
|
|
||||||
switch (codec) {
|
switch (codec) {
|
||||||
case H264:
|
case H264:
|
||||||
base = activity.getString(R.string.codec_h264);
|
return activity.getString(R.string.codec_h264);
|
||||||
break;
|
|
||||||
case H265:
|
case H265:
|
||||||
base = activity.getString(R.string.codec_h265);
|
return activity.getString(R.string.codec_h265);
|
||||||
break;
|
|
||||||
case LIBVPX_VP8:
|
case LIBVPX_VP8:
|
||||||
base = activity.getString(R.string.codec_libvpx_vp8);
|
return activity.getString(R.string.codec_libvpx_vp8);
|
||||||
break;
|
|
||||||
case LIBVPX_VP9:
|
case LIBVPX_VP9:
|
||||||
base = activity.getString(R.string.codec_libvpx_vp9);
|
return activity.getString(R.string.codec_libvpx_vp9);
|
||||||
break;
|
|
||||||
case PASSTHROUGH:
|
case PASSTHROUGH:
|
||||||
base = activity.getString(R.string.codec_passthrough);
|
return activity.getString(R.string.codec_passthrough);
|
||||||
break;
|
|
||||||
case AUTO:
|
case AUTO:
|
||||||
default:
|
default:
|
||||||
return activity.getString(R.string.option_auto);
|
return activity.getString(R.string.option_auto);
|
||||||
}
|
}
|
||||||
return appendPriorityScore(activity, base, codec != CastSettings.VideoCodec.AUTO, priorityScore);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String labelAudioCodec(AppCompatActivity activity, CastSettings.AudioCodec codec,
|
private static String labelAudioCodec(AppCompatActivity activity, CastSettings.AudioCodec codec) {
|
||||||
int priorityScore) {
|
|
||||||
String base;
|
|
||||||
switch (codec) {
|
switch (codec) {
|
||||||
case AAC:
|
case AAC:
|
||||||
base = activity.getString(R.string.codec_aac);
|
return activity.getString(R.string.codec_aac);
|
||||||
break;
|
|
||||||
case SPEEX:
|
case SPEEX:
|
||||||
base = activity.getString(R.string.codec_speex);
|
return activity.getString(R.string.codec_speex);
|
||||||
break;
|
|
||||||
case OPUS:
|
case OPUS:
|
||||||
base = activity.getString(R.string.codec_opus);
|
return activity.getString(R.string.codec_opus);
|
||||||
break;
|
|
||||||
case AUTO:
|
case AUTO:
|
||||||
default:
|
default:
|
||||||
return activity.getString(R.string.codec_audio_auto);
|
return activity.getString(R.string.codec_audio_auto);
|
||||||
}
|
}
|
||||||
return appendPriorityScore(activity, base, codec != CastSettings.AudioCodec.AUTO, priorityScore);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String appendPriorityScore(AppCompatActivity activity, String base, boolean showScore,
|
private static int codecSpinnerScore(boolean showScores, Enum<?> codec, int priorityScore) {
|
||||||
int priorityScore) {
|
if (!showScores || codec == null) {
|
||||||
if (!showScore || !AppPreferences.isShowCodecPriorityScores(activity)) {
|
return com.foxx.androidcast.ui.SettingsScoredArrayAdapter.NO_SCORE;
|
||||||
return base;
|
|
||||||
}
|
}
|
||||||
return base + activity.getString(R.string.codec_priority_score_suffix, priorityScore);
|
String name = codec.name();
|
||||||
|
if ("AUTO".equals(name)) {
|
||||||
|
return com.foxx.androidcast.ui.SettingsScoredArrayAdapter.NO_SCORE;
|
||||||
|
}
|
||||||
|
return priorityScore;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,20 +29,42 @@ public class DeveloperSettingsActivity extends AppCompatActivity {
|
|||||||
CheckBox sessionStats = findViewById(R.id.check_grab_session_stats);
|
CheckBox sessionStats = findViewById(R.id.check_grab_session_stats);
|
||||||
Button reloadCodecs = findViewById(R.id.btn_reload_codecs_json);
|
Button reloadCodecs = findViewById(R.id.btn_reload_codecs_json);
|
||||||
|
|
||||||
debugOverlay.setChecked(AppPreferences.isShowReceiverDebugOverlay(this));
|
refreshFromPreferences(debugOverlay, codecScores, sessionStats);
|
||||||
codecScores.setChecked(AppPreferences.isDevShowCodecPriorityScoresExplicit(this));
|
|
||||||
sessionStats.setChecked(AppPreferences.isGrabSessionStats(this));
|
|
||||||
|
|
||||||
debugOverlay.setOnCheckedChangeListener((buttonView, isChecked) ->
|
|
||||||
AppPreferences.setShowReceiverDebugOverlay(this, isChecked));
|
|
||||||
codecScores.setOnCheckedChangeListener((buttonView, isChecked) ->
|
|
||||||
AppPreferences.setShowCodecPriorityScores(this, isChecked));
|
|
||||||
sessionStats.setOnCheckedChangeListener((buttonView, isChecked) ->
|
|
||||||
AppPreferences.setGrabSessionStats(this, isChecked));
|
|
||||||
|
|
||||||
reloadCodecs.setOnClickListener(v -> {
|
reloadCodecs.setOnClickListener(v -> {
|
||||||
String summary = CodecPriorityCatalog.reload(this);
|
String summary = CodecPriorityCatalog.reload(this);
|
||||||
Toast.makeText(this, summary, Toast.LENGTH_LONG).show();
|
Toast.makeText(this, summary, Toast.LENGTH_LONG).show();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
refreshFromPreferences(
|
||||||
|
findViewById(R.id.check_receiver_debug_overlay),
|
||||||
|
findViewById(R.id.check_show_codec_priority_scores),
|
||||||
|
findViewById(R.id.check_grab_session_stats));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void refreshFromPreferences(CheckBox debugOverlay, CheckBox codecScores, CheckBox sessionStats) {
|
||||||
|
bindDeveloperCheckbox(debugOverlay, AppPreferences.isShowReceiverDebugOverlay(this),
|
||||||
|
AppPreferences::setShowReceiverDebugOverlay);
|
||||||
|
bindDeveloperCheckbox(codecScores, AppPreferences.isShowCodecPriorityScores(this),
|
||||||
|
AppPreferences::setShowCodecPriorityScores);
|
||||||
|
bindDeveloperCheckbox(sessionStats, AppPreferences.isGrabSessionStats(this),
|
||||||
|
AppPreferences::setGrabSessionStats);
|
||||||
|
}
|
||||||
|
|
||||||
|
private interface BoolPrefWriter {
|
||||||
|
void write(Context context, boolean value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void bindDeveloperCheckbox(CheckBox box, boolean checked, BoolPrefWriter writer) {
|
||||||
|
if (box == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
box.setOnCheckedChangeListener(null);
|
||||||
|
box.setChecked(checked);
|
||||||
|
box.setOnCheckedChangeListener((buttonView, isChecked) -> writer.write(this, isChecked));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,9 +21,16 @@ public final class SettingsEnumSpinner {
|
|||||||
|
|
||||||
public static <T> void bind(Spinner spinner, T[] values, String[] labels, boolean[] selectable,
|
public static <T> void bind(Spinner spinner, T[] values, String[] labels, boolean[] selectable,
|
||||||
T current, java.util.function.Consumer<T> onSelect) {
|
T current, java.util.function.Consumer<T> onSelect) {
|
||||||
|
bind(spinner, values, labels, null, selectable, current, onSelect);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> void bind(Spinner spinner, T[] values, String[] labels, int[] priorityScores,
|
||||||
|
boolean[] selectable, T current, java.util.function.Consumer<T> onSelect) {
|
||||||
spinner.setTag(R.id.tag_spinner_enum, values);
|
spinner.setTag(R.id.tag_spinner_enum, values);
|
||||||
spinner.setTag(R.id.tag_spinner_selectable, selectable);
|
spinner.setTag(R.id.tag_spinner_selectable, selectable);
|
||||||
DisabledAdapter adapter = new DisabledAdapter(spinner.getContext(), labels, selectable);
|
SettingsStringArrayAdapter adapter = priorityScores != null
|
||||||
|
? new ScoredDisabledAdapter(spinner.getContext(), labels, priorityScores, selectable)
|
||||||
|
: new DisabledAdapter(spinner.getContext(), labels, selectable);
|
||||||
spinner.setBackgroundResource(R.drawable.bg_settings_spinner);
|
spinner.setBackgroundResource(R.drawable.bg_settings_spinner);
|
||||||
spinner.setAdapter(adapter);
|
spinner.setAdapter(adapter);
|
||||||
spinner.setPopupBackgroundResource(R.drawable.bg_settings_spinner_popup);
|
spinner.setPopupBackgroundResource(R.drawable.bg_settings_spinner_popup);
|
||||||
@@ -35,7 +42,7 @@ public final class SettingsEnumSpinner {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
spinner.setSelection(selectedIndex[0]);
|
spinner.setSelection(selectedIndex[0]);
|
||||||
adapter.setSelectedPosition(selectedIndex[0]);
|
setAdapterSelectedPosition(adapter, selectedIndex[0]);
|
||||||
SettingsStringArrayAdapter.wireSelectionRefresh(spinner, adapter);
|
SettingsStringArrayAdapter.wireSelectionRefresh(spinner, adapter);
|
||||||
spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
||||||
@Override
|
@Override
|
||||||
@@ -48,7 +55,7 @@ public final class SettingsEnumSpinner {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
selectedIndex[0] = position;
|
selectedIndex[0] = position;
|
||||||
adapter.setSelectedPosition(position);
|
setAdapterSelectedPosition(adapter, position);
|
||||||
onSelect.accept(values[position]);
|
onSelect.accept(values[position]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,6 +64,10 @@ public final class SettingsEnumSpinner {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void setAdapterSelectedPosition(SettingsStringArrayAdapter adapter, int position) {
|
||||||
|
adapter.setSelectedPosition(position);
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public static <T extends Enum<T>> T read(Spinner spinner, T fallback) {
|
public static <T extends Enum<T>> T read(Spinner spinner, T fallback) {
|
||||||
Object tag = spinner.getTag(R.id.tag_spinner_enum);
|
Object tag = spinner.getTag(R.id.tag_spinner_enum);
|
||||||
@@ -86,4 +97,18 @@ public final class SettingsEnumSpinner {
|
|||||||
return position >= 0 && position < selectable.length && selectable[position];
|
return position >= 0 && position < selectable.length && selectable[position];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final class ScoredDisabledAdapter extends SettingsScoredArrayAdapter {
|
||||||
|
private final boolean[] selectable;
|
||||||
|
|
||||||
|
ScoredDisabledAdapter(Context context, String[] labels, int[] scores, boolean[] selectable) {
|
||||||
|
super(context, labels, scores);
|
||||||
|
this.selectable = selectable;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEnabled(int position) {
|
||||||
|
return position >= 0 && position < selectable.length && selectable[position];
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,70 @@
|
|||||||
|
package com.foxx.androidcast.ui;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import com.foxx.androidcast.R;
|
||||||
|
|
||||||
|
/** Settings spinner row with label left and optional priority score right-aligned. */
|
||||||
|
public class SettingsScoredArrayAdapter extends SettingsStringArrayAdapter {
|
||||||
|
/** Hide score column when negative. */
|
||||||
|
public static final int NO_SCORE = -1;
|
||||||
|
|
||||||
|
private final int[] scores;
|
||||||
|
|
||||||
|
public SettingsScoredArrayAdapter(Context context, String[] labels, int[] scores) {
|
||||||
|
super(context, labels, R.layout.spinner_dropdown_item_scored);
|
||||||
|
this.scores = scores != null ? scores : new int[labels.length];
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@NonNull
|
||||||
|
public View getView(int position, View convertView, @NonNull ViewGroup parent) {
|
||||||
|
View row = convertView;
|
||||||
|
if (!isScoredRow(row)) {
|
||||||
|
row = LayoutInflater.from(getContext()).inflate(R.layout.spinner_item_scored, parent, false);
|
||||||
|
}
|
||||||
|
bindRow(row, position);
|
||||||
|
row.setAlpha(isEnabled(position) ? 1f : 0.45f);
|
||||||
|
return row;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View getDropDownView(int position, View convertView, @NonNull ViewGroup parent) {
|
||||||
|
View row = convertView;
|
||||||
|
if (!isScoredRow(row)) {
|
||||||
|
row = LayoutInflater.from(getContext())
|
||||||
|
.inflate(R.layout.spinner_dropdown_item_scored, parent, false);
|
||||||
|
}
|
||||||
|
bindRow(row, position);
|
||||||
|
SettingsSpinnerDropdownStyle.apply(row, position == selectedPosition, isEnabled(position));
|
||||||
|
return row;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void bindRow(View row, int position) {
|
||||||
|
TextView label = row.findViewById(R.id.spinner_item_label);
|
||||||
|
TextView score = row.findViewById(R.id.spinner_item_score);
|
||||||
|
if (label != null) {
|
||||||
|
label.setText(getItem(position));
|
||||||
|
}
|
||||||
|
if (score != null) {
|
||||||
|
int value = position >= 0 && position < scores.length ? scores[position] : NO_SCORE;
|
||||||
|
if (value >= 0) {
|
||||||
|
score.setVisibility(View.VISIBLE);
|
||||||
|
score.setText(String.valueOf(value));
|
||||||
|
} else {
|
||||||
|
score.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isScoredRow(View row) {
|
||||||
|
return row != null && row.findViewById(R.id.spinner_item_label) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -16,7 +16,7 @@ import com.foxx.androidcast.R;
|
|||||||
/** Settings spinner adapter: plain row when closed, styled card rows in the dropdown. */
|
/** Settings spinner adapter: plain row when closed, styled card rows in the dropdown. */
|
||||||
public class SettingsStringArrayAdapter extends ArrayAdapter<String> {
|
public class SettingsStringArrayAdapter extends ArrayAdapter<String> {
|
||||||
private final int dropdownLayout;
|
private final int dropdownLayout;
|
||||||
private int selectedPosition;
|
protected int selectedPosition;
|
||||||
|
|
||||||
public SettingsStringArrayAdapter(Context context, String[] labels) {
|
public SettingsStringArrayAdapter(Context context, String[] labels) {
|
||||||
this(context, labels, R.layout.spinner_dropdown_item_settings);
|
this(context, labels, R.layout.spinner_dropdown_item_settings);
|
||||||
|
|||||||
@@ -22,7 +22,6 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="24dp"
|
android:layout_marginTop="24dp"
|
||||||
android:checked="true"
|
|
||||||
android:text="@string/dev_show_receiver_debug_overlay" />
|
android:text="@string/dev_show_receiver_debug_overlay" />
|
||||||
|
|
||||||
<CheckBox
|
<CheckBox
|
||||||
@@ -37,7 +36,6 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="12dp"
|
android:layout_marginTop="12dp"
|
||||||
android:checked="true"
|
|
||||||
android:text="@string/dev_grab_session_stats" />
|
android:text="@string/dev_grab_session_stats" />
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
47
app/src/main/res/layout/spinner_dropdown_item_scored.xml
Normal file
47
app/src/main/res/layout/spinner_dropdown_item_scored.xml
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:id="@+id/spinner_dropdown_item_root"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="6dp"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:layout_marginEnd="6dp"
|
||||||
|
android:layout_marginBottom="4dp"
|
||||||
|
android:background="@drawable/bg_settings_spinner_item"
|
||||||
|
android:clipToOutline="true"
|
||||||
|
android:elevation="4dp"
|
||||||
|
android:outlineProvider="background">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:paddingStart="12dp"
|
||||||
|
android:paddingTop="10dp"
|
||||||
|
android:paddingEnd="12dp"
|
||||||
|
android:paddingBottom="12dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/spinner_item_label"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:textColor="@color/settings_spinner_text"
|
||||||
|
android:textSize="16sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/spinner_item_score"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="12dp"
|
||||||
|
android:fontFamily="monospace"
|
||||||
|
android:gravity="end"
|
||||||
|
android:minWidth="40dp"
|
||||||
|
android:textColor="@color/settings_spinner_text"
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:alpha="0.72" />
|
||||||
|
</LinearLayout>
|
||||||
|
</FrameLayout>
|
||||||
33
app/src/main/res/layout/spinner_item_scored.xml
Normal file
33
app/src/main/res/layout/spinner_item_scored.xml
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:paddingStart="8dp"
|
||||||
|
android:paddingTop="8dp"
|
||||||
|
android:paddingEnd="8dp"
|
||||||
|
android:paddingBottom="8dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/spinner_item_label"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:textColor="@color/settings_spinner_text"
|
||||||
|
android:textSize="16sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/spinner_item_score"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="12dp"
|
||||||
|
android:fontFamily="monospace"
|
||||||
|
android:gravity="end"
|
||||||
|
android:minWidth="36dp"
|
||||||
|
android:textColor="@color/settings_spinner_text"
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:alpha="0.72" />
|
||||||
|
</LinearLayout>
|
||||||
@@ -109,9 +109,17 @@ reconnect_known_devices() {
|
|||||||
|
|
||||||
# shellcheck source=scripts/android-ndk.sh
|
# shellcheck source=scripts/android-ndk.sh
|
||||||
source "$ROOT/scripts/android-ndk.sh"
|
source "$ROOT/scripts/android-ndk.sh"
|
||||||
|
# shellcheck source=scripts/native-build-cache.sh
|
||||||
|
source "$ROOT/scripts/native-build-cache.sh"
|
||||||
|
export ANDROIDCAST_ROOT="$ROOT"
|
||||||
export ANDROID_NDK_HOME="$(androidcast_find_ndk_root "$ROOT")"
|
export ANDROID_NDK_HOME="$(androidcast_find_ndk_root "$ROOT")"
|
||||||
export ANDROID_NDK_ROOT="$ANDROID_NDK_HOME"
|
export ANDROID_NDK_ROOT="$ANDROID_NDK_HOME"
|
||||||
echo "==> Using NDK: $ANDROID_NDK_HOME"
|
echo "==> Using NDK: $ANDROID_NDK_HOME"
|
||||||
|
if androidcast_ccache_wanted 2>/dev/null; then
|
||||||
|
echo "==> Native ccache: enabled (ANDROIDCAST_CCACHE=${ANDROIDCAST_CCACHE:-auto})"
|
||||||
|
else
|
||||||
|
echo "==> Native ccache: disabled"
|
||||||
|
fi
|
||||||
|
|
||||||
# Match app/build.gradle defaultConfig.ndk.abiFilters
|
# Match app/build.gradle defaultConfig.ndk.abiFilters
|
||||||
ARCH_ABIS="armeabi-v7a arm64-v8a x86_64"
|
ARCH_ABIS="armeabi-v7a arm64-v8a x86_64"
|
||||||
|
|||||||
@@ -74,6 +74,10 @@ export ANDROID_NDK_HOME="$NDK"
|
|||||||
export ANDROID_NDK_ROOT="$NDK"
|
export ANDROID_NDK_ROOT="$NDK"
|
||||||
export PATH="$NDK_HOST_BIN:$PATH"
|
export PATH="$NDK_HOST_BIN:$PATH"
|
||||||
|
|
||||||
|
# shellcheck source=native-build-cache.sh
|
||||||
|
source "$ROOT/scripts/native-build-cache.sh"
|
||||||
|
androidcast_apply_native_build_accelerators "$ROOT" "$ABI" "$NDK" "$NDK_HOST_BIN"
|
||||||
|
|
||||||
VPX_CONFIGURE_EXTRA=()
|
VPX_CONFIGURE_EXTRA=()
|
||||||
APP_CFLAGS_EXTRA=""
|
APP_CFLAGS_EXTRA=""
|
||||||
case "$ABI" in
|
case "$ABI" in
|
||||||
@@ -88,6 +92,9 @@ case "$ABI" in
|
|||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
|
## common native optimization options
|
||||||
|
APP_CFLAGS_EXTRA+=" -O2 -fdata-sections -ffunction-sections -Wl,--gc-sections -ftree-vectorize -O2 -s -funroll-loops -fomit-frame-pointer "
|
||||||
|
|
||||||
echo "Building libvpx for $ABI (target=$VPX_TARGET) ..."
|
echo "Building libvpx for $ABI (target=$VPX_TARGET) ..."
|
||||||
echo " source: $VPX_SRC"
|
echo " source: $VPX_SRC"
|
||||||
echo " build: $BUILD_DIR"
|
echo " build: $BUILD_DIR"
|
||||||
@@ -125,6 +132,7 @@ elif [[ "$ABI" == x86 || "$ABI" == x86_64 ]] && [[ -f "$BUILD_DIR/vpx_config.h"
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
if $need_configure; then
|
if $need_configure; then
|
||||||
|
# CC/CXX may be ccache-wrapped NDK clang (see native-build-cache.sh).
|
||||||
"$VPX_SRC/configure" \
|
"$VPX_SRC/configure" \
|
||||||
--target="$VPX_TARGET" \
|
--target="$VPX_TARGET" \
|
||||||
--disable-examples \
|
--disable-examples \
|
||||||
|
|||||||
32
scripts/ci-build.sh
Executable file
32
scripts/ci-build.sh
Executable file
@@ -0,0 +1,32 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# CI entry: native codecs (all ABIs) + Gradle debug APK + unit tests.
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
cd "$ROOT"
|
||||||
|
|
||||||
|
export ANDROIDCAST_ROOT="$ROOT"
|
||||||
|
export ANDROIDCAST_CCACHE="${ANDROIDCAST_CCACHE:-1}"
|
||||||
|
export ANDROIDCAST_USE_DISTCC="${ANDROIDCAST_USE_DISTCC:-0}"
|
||||||
|
|
||||||
|
# shellcheck source=android-ndk.sh
|
||||||
|
source "$ROOT/scripts/android-ndk.sh"
|
||||||
|
export ANDROID_NDK_HOME="$(androidcast_find_ndk_root "$ROOT")"
|
||||||
|
export ANDROID_NDK_ROOT="$ANDROID_NDK_HOME"
|
||||||
|
|
||||||
|
if [[ -f .gitmodules ]]; then
|
||||||
|
git submodule update --init --recursive
|
||||||
|
fi
|
||||||
|
|
||||||
|
ABIS="${ANDROIDCAST_CI_ABIS:-armeabi-v7a arm64-v8a x86_64}"
|
||||||
|
echo "==> Building libvpx for: $ABIS"
|
||||||
|
rm -rf "$ROOT/build/native"
|
||||||
|
mkdir -p "$ROOT/build/native"
|
||||||
|
for abi in $ABIS; do
|
||||||
|
./scripts/build-native-codecs.sh "$abi"
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "==> Gradle assembleDebug + unit tests"
|
||||||
|
./gradlew --no-daemon clean assembleDebug testDebugUnitTest
|
||||||
|
|
||||||
|
echo "==> Done: $ROOT/app/build/outputs/apk/debug/app-debug.apk"
|
||||||
124
scripts/native-build-cache.sh
Normal file
124
scripts/native-build-cache.sh
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Host build accelerators for native codecs (libvpx / NDK). Source, do not execute.
|
||||||
|
#
|
||||||
|
# ccache (default: auto-detect):
|
||||||
|
# ANDROIDCAST_CCACHE=auto|1|0
|
||||||
|
# CCACHE_DIR — default: <repo>/build/.ccache
|
||||||
|
#
|
||||||
|
# distcc (off by default; experimental for libvpx configure/make only, not ndk-build):
|
||||||
|
# ANDROIDCAST_USE_DISTCC=1
|
||||||
|
# DISTCC_HOSTS=localhost/8 192.168.1.10/16
|
||||||
|
#
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
[[ -n "${ANDROIDCAST_NATIVE_CACHE_SOURCED:-}" ]] && return 0
|
||||||
|
ANDROIDCAST_NATIVE_CACHE_SOURCED=1
|
||||||
|
|
||||||
|
androidcast_project_root() {
|
||||||
|
if [[ -n "${ANDROIDCAST_ROOT:-}" ]]; then
|
||||||
|
printf '%s\n' "$ANDROIDCAST_ROOT"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
local here
|
||||||
|
here="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
printf '%s\n' "$here"
|
||||||
|
}
|
||||||
|
|
||||||
|
androidcast_ccache_wanted() {
|
||||||
|
case "${ANDROIDCAST_CCACHE:-auto}" in
|
||||||
|
0 | false | FALSE | no | NO | off | OFF) return 1 ;;
|
||||||
|
1 | true | TRUE | yes | YES | on | ON) return 0 ;;
|
||||||
|
auto | AUTO | "")
|
||||||
|
command -v ccache >/dev/null 2>&1
|
||||||
|
;;
|
||||||
|
*) return 0 ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
androidcast_ndk_clang_basename() {
|
||||||
|
case "${1:-}" in
|
||||||
|
arm64-v8a) echo aarch64-linux-android29-clang ;;
|
||||||
|
armeabi-v7a) echo armv7a-linux-androideabi29-clang ;;
|
||||||
|
x86_64) echo x86_64-linux-android29-clang ;;
|
||||||
|
x86) echo i686-linux-android29-clang ;;
|
||||||
|
*) echo "" ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
androidcast_ndk_clangxx_basename() {
|
||||||
|
case "${1:-}" in
|
||||||
|
arm64-v8a) echo aarch64-linux-android29-clang++ ;;
|
||||||
|
armeabi-v7a) echo armv7a-linux-androideabi29-clang++ ;;
|
||||||
|
x86_64) echo x86_64-linux-android29-clang++ ;;
|
||||||
|
x86) echo i686-linux-android29-clang++ ;;
|
||||||
|
*) echo "" ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
# Configure ccache + optional distcc; set CC/CXX/NDK_CCACHE for libvpx + ndk-build.
|
||||||
|
# Args: project_root abi ndk_root ndk_host_bin
|
||||||
|
androidcast_apply_native_build_accelerators() {
|
||||||
|
local root="${1:?root}"
|
||||||
|
local abi="${2:?abi}"
|
||||||
|
local ndk_bin="${4:?ndk_host_bin}"
|
||||||
|
local clang clangxx
|
||||||
|
clang="$(androidcast_ndk_clang_basename "$abi")"
|
||||||
|
clangxx="$(androidcast_ndk_clangxx_basename "$abi")"
|
||||||
|
if [[ -z "$clang" || ! -x "$ndk_bin/$clang" ]]; then
|
||||||
|
echo "WARN: NDK clang not found for $abi under $ndk_bin" >&2
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
local prefix=""
|
||||||
|
local ccache_on=false
|
||||||
|
local distcc_on=false
|
||||||
|
|
||||||
|
if androidcast_ccache_wanted && command -v ccache >/dev/null 2>&1; then
|
||||||
|
ccache_on=true
|
||||||
|
export CCACHE_DIR="${CCACHE_DIR:-$root/build/.ccache}"
|
||||||
|
mkdir -p "$CCACHE_DIR"
|
||||||
|
export CCACHE_BASEDIR="$root"
|
||||||
|
export CCACHE_COMPRESS="${CCACHE_COMPRESS:-1}"
|
||||||
|
export CCACHE_COMPILERCHECK="${CCACHE_COMPILERCHECK:-content}"
|
||||||
|
export CCACHE_MAXSIZE="${CCACHE_MAXSIZE:-5G}"
|
||||||
|
export NDK_CCACHE="${NDK_CCACHE:-ccache}"
|
||||||
|
prefix="ccache "
|
||||||
|
echo "ccache: ON (dir=$CCACHE_DIR, NDK_CCACHE=$NDK_CCACHE)"
|
||||||
|
ccache -s 2>/dev/null | sed 's/^/ /' || true
|
||||||
|
elif androidcast_ccache_wanted; then
|
||||||
|
echo "ccache: requested but not installed (install ccache or ANDROIDCAST_CCACHE=0)"
|
||||||
|
else
|
||||||
|
echo "ccache: off (ANDROIDCAST_CCACHE=${ANDROIDCAST_CCACHE:-auto})"
|
||||||
|
fi
|
||||||
|
|
||||||
|
case "${ANDROIDCAST_USE_DISTCC:-0}" in
|
||||||
|
1 | true | TRUE | yes | YES | on | ON)
|
||||||
|
if command -v distcc >/dev/null 2>&1 && [[ -n "${DISTCC_HOSTS:-}" ]]; then
|
||||||
|
distcc_on=true
|
||||||
|
export DISTCC_VERBOSE="${DISTCC_VERBOSE:-0}"
|
||||||
|
prefix="distcc ${prefix}"
|
||||||
|
echo "distcc: ON (experimental, libvpx make/configure only; hosts=$DISTCC_HOSTS)"
|
||||||
|
elif command -v distcc >/dev/null 2>&1; then
|
||||||
|
echo "distcc: ANDROIDCAST_USE_DISTCC=1 but DISTCC_HOSTS empty — skipped" >&2
|
||||||
|
else
|
||||||
|
echo "distcc: requested but distcc not installed — skipped" >&2
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "distcc: off (set ANDROIDCAST_USE_DISTCC=1 and DISTCC_HOSTS to try)"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
export CC="${prefix}${ndk_bin}/${clang}"
|
||||||
|
if [[ -n "$clangxx" && -x "$ndk_bin/$clangxx" ]]; then
|
||||||
|
export CXX="${prefix}${ndk_bin}/${clangxx}"
|
||||||
|
else
|
||||||
|
export CXX="${prefix}${ndk_bin}/${clang}"
|
||||||
|
fi
|
||||||
|
export AR="${ndk_bin}/llvm-ar"
|
||||||
|
export RANLIB="${ndk_bin}/llvm-ranlib"
|
||||||
|
|
||||||
|
# libvpx configure reads these when cross-compiling.
|
||||||
|
export AS="$CC"
|
||||||
|
export LD="${ndk_bin}/ld.lld"
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user