Skip to content

Commit 1800f91

Browse files
committed
Done
Signed-off-by: Fung Gwo <fython@163.com>
1 parent a207ce2 commit 1800f91

17 files changed

Lines changed: 646 additions & 874 deletions

app/build.gradle

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,14 @@ android {
1111
targetSdkVersion rootProject.ext.targetSdkVersion
1212
versionCode rootProject.ext.versionCode
1313
versionName rootProject.ext.versionName
14+
15+
resConfigs "en", "zh_CN", "zh_TW"
1416
}
1517

1618
buildTypes {
1719
release {
18-
minifyEnabled false
20+
minifyEnabled true
21+
shrinkResources true
1922
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
2023
}
2124
}
@@ -28,6 +31,7 @@ android {
2831

2932
dependencies {
3033
final androidxVersion = '1.0.0'
34+
implementation "androidx.core:core:$androidxVersion"
3135
implementation "androidx.annotation:annotation:$androidxVersion"
3236
implementation "androidx.recyclerview:recyclerview:$androidxVersion"
3337
}

app/proguard-rules.pro

Lines changed: 2 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,2 @@
1-
# Add project specific ProGuard rules here.
2-
# You can control the set of applied configuration files using the
3-
# proguardFiles setting in build.gradle.
4-
#
5-
# For more details, see
6-
# http://developer.android.com/guide/developing/tools/proguard.html
7-
8-
# If your project uses WebView with JS, uncomment the following
9-
# and specify the fully qualified class name to the JavaScript interface
10-
# class:
11-
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12-
# public *;
13-
#}
14-
15-
# Uncomment this to preserve the line number information for
16-
# debugging stack traces.
17-
#-keepattributes SourceFile,LineNumberTable
18-
19-
# If you keep the line number information, uncomment this to
20-
# hide the original source file name.
21-
#-renamesourcefileattribute SourceFile
1+
-dontobfuscate
2+
-keepattributes SourceFile,LineNumberTable

app/src/main/AndroidManifest.xml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
1111

1212
<application
13+
android:name=".ProxyApplication"
1314
android:allowBackup="true"
1415
android:icon="@mipmap/ic_launcher"
1516
android:label="@string/app_name"
@@ -19,6 +20,18 @@
1920
android:theme="@style/Base.Theme.App"
2021
tools:ignore="GoogleAppIndexingWarning">
2122

23+
<activity
24+
android:name=".SettingsActivity"
25+
android:exported="true">
26+
<intent-filter>
27+
<action android:name="android.intent.action.MAIN"/>
28+
29+
<category android:name="android.intent.category.DEFAULT"/>
30+
<category android:name="android.intent.category.LAUNCHER"/>
31+
</intent-filter>
32+
</activity>
33+
34+
2235
<activity
2336
android:name=".ProxyCameraActivity"
2437
android:theme="@style/Theme.Transparent"

app/src/main/java/androidx/core/content/FileProvider.java

Lines changed: 0 additions & 834 deletions
This file was deleted.
Lines changed: 126 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,164 @@
11
package app.gwo.wechat.docuiproxy;
22

3+
import android.annotation.SuppressLint;
34
import android.app.AlertDialog;
45
import android.app.Dialog;
56
import android.app.DialogFragment;
7+
import android.content.BroadcastReceiver;
68
import android.content.Context;
9+
import android.content.DialogInterface;
10+
import android.content.Intent;
11+
import android.content.IntentFilter;
12+
import android.content.pm.ResolveInfo;
13+
import android.os.AsyncTask;
714
import android.os.Bundle;
815
import android.view.ContextThemeWrapper;
916
import android.view.LayoutInflater;
1017
import android.view.View;
18+
import android.widget.CheckBox;
19+
20+
import java.util.ArrayList;
21+
import java.util.List;
1122

1223
import androidx.annotation.NonNull;
1324
import androidx.annotation.Nullable;
25+
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
26+
import androidx.recyclerview.widget.RecyclerView;
27+
import app.gwo.wechat.docuiproxy.adapter.CameraChooserAdapter;
28+
import app.gwo.wechat.docuiproxy.util.IntentUtils;
29+
import app.gwo.wechat.docuiproxy.util.Settings;
30+
31+
import static app.gwo.wechat.docuiproxy.Constants.EXTRA_DATA;
32+
import static java.util.Objects.requireNonNull;
1433

1534
public final class CameraChooserDialogFragment extends DialogFragment {
1635

36+
@NonNull
37+
public static CameraChooserDialogFragment newInstance() {
38+
return new CameraChooserDialogFragment();
39+
}
40+
41+
private static final String EXTRA_LIST_DATA = Constants.EXTRA_PREFIX + ".LIST_DATA";
42+
1743
private View mContentView;
44+
private RecyclerView mRecyclerView;
45+
private CheckBox mCheckBox;
46+
47+
private CameraChooserAdapter mAdapter;
48+
private List<ResolveInfo> mData;
49+
50+
private QueryCameraAppsTask mQueryTask;
51+
52+
private final BroadcastReceiver mItemClickReceiver = new BroadcastReceiver() {
53+
@Override
54+
public void onReceive(@NonNull Context context, @Nullable Intent intent) {
55+
requireNonNull(intent);
56+
final ResolveInfo resolveInfo = intent.getParcelableExtra(EXTRA_DATA);
57+
if (mCheckBox.isChecked()) {
58+
Settings.getInstance().setPreferredCamera(IntentUtils.toComponent(resolveInfo));
59+
}
60+
((ProxyCameraActivity) getActivity())
61+
.onStartCameraApp(IntentUtils.toComponent(resolveInfo));
62+
}
63+
};
1864

1965
@NonNull
2066
public Context getThemedContext() {
2167
return new ContextThemeWrapper(getActivity(), android.R.style.Theme_Material_Dialog);
2268
}
2369

2470
@Override
25-
public Dialog onCreateDialog(Bundle savedInstanceState) {
26-
final AlertDialog.Builder builder = new AlertDialog.Builder(
27-
getActivity(), android.R.style.Theme_Material_Dialog);
71+
public void onCreate(@Nullable Bundle savedInstanceState) {
72+
super.onCreate(savedInstanceState);
73+
74+
if (savedInstanceState != null) {
75+
mData = savedInstanceState.getParcelableArrayList(EXTRA_LIST_DATA);
76+
}
77+
78+
LocalBroadcastManager.getInstance(getActivity()).registerReceiver(
79+
mItemClickReceiver,
80+
new IntentFilter(CameraChooserAdapter.ACTION_ITEM_CLICK));
81+
}
82+
83+
@Override
84+
public void onDestroy() {
85+
super.onDestroy();
86+
87+
LocalBroadcastManager.getInstance(getActivity()).unregisterReceiver(mItemClickReceiver);
88+
}
89+
90+
@Override
91+
public void onDestroyView() {
92+
super.onDestroyView();
93+
if (mQueryTask != null && !mQueryTask.isCancelled()) {
94+
mQueryTask.cancel(true);
95+
}
96+
}
97+
98+
@Override
99+
public void onSaveInstanceState(@NonNull Bundle outState) {
100+
super.onSaveInstanceState(outState);
101+
if (mData != null) {
102+
outState.putParcelableArrayList(EXTRA_LIST_DATA, new ArrayList<>(mData));
103+
}
104+
}
105+
106+
@Override
107+
public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
108+
final AlertDialog.Builder builder = new AlertDialog.Builder(getThemedContext());
28109
builder.setTitle(R.string.camera_chooser_dialog_title);
29110
builder.setView(mContentView = onCreateContentView(savedInstanceState));
30-
return super.onCreateDialog(savedInstanceState);
111+
builder.setNegativeButton(android.R.string.cancel, null);
112+
return builder.create();
31113
}
32114

115+
@Override
116+
public void onDismiss(DialogInterface dialog) {
117+
super.onDismiss(dialog);
118+
if (getActivity() != null) {
119+
getActivity().finish();
120+
}
121+
}
122+
123+
@NonNull
33124
private View onCreateContentView(@Nullable Bundle savedInstanceState) {
34125
final View view = LayoutInflater.from(getThemedContext())
35126
.inflate(R.layout.dialog_camera_chooser_content, null);
36127

128+
mRecyclerView = view.findViewById(android.R.id.list);
129+
mRecyclerView.setHasFixedSize(true);
130+
if (mAdapter == null) {
131+
mAdapter = new CameraChooserAdapter();
132+
}
133+
mRecyclerView.setAdapter(mAdapter);
37134

135+
mCheckBox = view.findViewById(android.R.id.checkbox);
136+
137+
if (mData != null) {
138+
mAdapter.submitList(mData);
139+
} else {
140+
mQueryTask = new QueryCameraAppsTask();
141+
mQueryTask.execute();
142+
}
38143

39144
return view;
40145
}
41146

147+
@SuppressLint("StaticFieldLeak")
148+
private class QueryCameraAppsTask extends AsyncTask<Void, Void, List<ResolveInfo>> {
149+
150+
@Override
151+
protected List<ResolveInfo> doInBackground(Void... params) {
152+
return IntentUtils.queryCameraActivities(getActivity());
153+
}
154+
155+
@Override
156+
protected void onPostExecute(@NonNull List<ResolveInfo> result) {
157+
if (mAdapter != null) {
158+
mAdapter.submitList(result);
159+
}
160+
}
161+
162+
}
163+
42164
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package app.gwo.wechat.docuiproxy;
2+
3+
import android.app.Application;
4+
import android.os.StrictMode;
5+
6+
import app.gwo.wechat.docuiproxy.util.Settings;
7+
8+
public final class ProxyApplication extends Application {
9+
10+
@Override
11+
public void onCreate() {
12+
super.onCreate();
13+
Settings.init(this);
14+
15+
final StrictMode.VmPolicy vmPolicy = new StrictMode.VmPolicy.Builder().build();
16+
StrictMode.setVmPolicy(vmPolicy);
17+
}
18+
19+
}

app/src/main/java/app/gwo/wechat/docuiproxy/ProxyCameraActivity.java

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,20 @@
11
package app.gwo.wechat.docuiproxy;
22

3+
import android.content.ActivityNotFoundException;
4+
import android.content.ComponentName;
35
import android.content.Intent;
46
import android.net.Uri;
57
import android.os.Bundle;
68
import android.provider.MediaStore;
79
import android.util.Log;
810

9-
import java.io.File;
10-
1111
import androidx.annotation.NonNull;
1212
import androidx.annotation.Nullable;
13-
import androidx.core.content.FileProvider;
1413
import app.gwo.wechat.docuiproxy.util.DumpUtils;
14+
import app.gwo.wechat.docuiproxy.util.Settings;
1515

1616
import static app.gwo.wechat.docuiproxy.BuildConfig.DEBUG;
17+
import static java.util.Objects.requireNonNull;
1718

1819
/**
1920
* Proxy open result to WeChat (include other apps who call camera apps)
@@ -85,13 +86,23 @@ private void processIntentForWeChat(@NonNull Intent intent) {
8586
}
8687

8788
private void processIntentForOthers(@NonNull Intent intent) {
88-
// TODO Open camera app
89-
Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
90-
cameraIntent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
91-
File cacheFile = new File(getCacheDir(), "test");
92-
cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT,
93-
FileProvider.getUriForFile(this, Constants.FILE_PROVIDER_AUTHORITY, cacheFile));
94-
startActivityForResult(cameraIntent, REQUEST_CODE_CAPTURE);
89+
final ComponentName preferredCamera = Settings.getInstance().getPreferredCamera();
90+
boolean launched = false;
91+
if (preferredCamera != null) {
92+
Log.d(TAG, "Launch preferred camera: " + preferredCamera.toString());
93+
try {
94+
onStartCameraApp(preferredCamera);
95+
launched = true;
96+
} catch (ActivityNotFoundException e) {
97+
e.printStackTrace();
98+
Settings.getInstance().setPreferredCamera(null);
99+
}
100+
}
101+
if (!launched) {
102+
CameraChooserDialogFragment
103+
.newInstance()
104+
.show(getFragmentManager(), "CameraChooser");
105+
}
95106
}
96107

97108
@Override
@@ -122,7 +133,8 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
122133
}
123134
case REQUEST_CODE_CAPTURE: {
124135
if (data != null) {
125-
136+
setResult(RESULT_OK, new Intent());
137+
finish();
126138
} else {
127139
// TODO Show failed
128140
finish();
@@ -146,4 +158,13 @@ void onCopyResult(boolean success) {
146158
finish();
147159
}
148160

161+
void onStartCameraApp(@NonNull ComponentName target) {
162+
requireNonNull(target);
163+
Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
164+
cameraIntent.setComponent(target);
165+
cameraIntent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
166+
cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, mExpectedOutput);
167+
startActivityForResult(cameraIntent, REQUEST_CODE_CAPTURE);
168+
}
169+
149170
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package app.gwo.wechat.docuiproxy;
2+
3+
import android.os.Bundle;
4+
import android.preference.PreferenceFragment;
5+
import android.widget.Toast;
6+
7+
import androidx.annotation.Nullable;
8+
import app.gwo.wechat.docuiproxy.util.Settings;
9+
10+
public final class SettingsActivity extends BaseActivity {
11+
12+
@Override
13+
protected void onCreate(@Nullable Bundle savedInstanceState) {
14+
super.onCreate(savedInstanceState);
15+
16+
if (savedInstanceState == null) {
17+
getFragmentManager()
18+
.beginTransaction()
19+
.replace(android.R.id.content, new SettingsFragment())
20+
.commit();
21+
}
22+
}
23+
24+
public static final class SettingsFragment extends PreferenceFragment {
25+
26+
private static final String KEY_PREFERRED_CAMERA_CLEAR = "clear_preferred_camera";
27+
28+
@Override
29+
public void onCreate(@Nullable Bundle savedInstanceState) {
30+
super.onCreate(savedInstanceState);
31+
addPreferencesFromResource(R.xml.settings_screen);
32+
33+
findPreference(KEY_PREFERRED_CAMERA_CLEAR).setOnPreferenceClickListener(p -> {
34+
Settings.getInstance().setPreferredCamera(null);
35+
Toast.makeText(getActivity(), R.string.toast_cleared, Toast.LENGTH_LONG).show();
36+
return true;
37+
});
38+
}
39+
40+
}
41+
42+
}

0 commit comments

Comments
 (0)