Commit e9c99e15 authored by Snow Star's avatar Snow Star 🏸

init

parents
*.iml
.gradle
/local.properties
/.idea/libraries
/.idea/modules.xml
/.idea/workspace.xml
.DS_Store
/build
/captures
.externalNativeBuild
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="WizardSettings">
<option name="children">
<map>
<entry key="imageWizard">
<value>
<PersistentState />
</value>
</entry>
<entry key="vectorWizard">
<value>
<PersistentState>
<option name="children">
<map>
<entry key="vectorAssetStep">
<value>
<PersistentState>
<option name="children">
<map>
<entry key="clipartAsset">
<value>
<PersistentState>
<option name="values">
<map>
<entry key="url" value="jar:file:/C:/Program%20Files/Android/Android%20Studio/plugins/android/lib/android.jar!/images/material_design_icons/av/ic_playlist_add_black_24dp.xml" />
</map>
</option>
</PersistentState>
</value>
</entry>
</map>
</option>
<option name="values">
<map>
<entry key="outputName" value="ic_add" />
<entry key="sourceFile" value="C:\Users\Tyan Boot" />
</map>
</option>
</PersistentState>
</value>
</entry>
</map>
</option>
</PersistentState>
</value>
</entry>
</map>
</option>
</component>
</project>
\ No newline at end of file
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<Objective-C-extensions>
<file>
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Import" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Macro" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Typedef" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Enum" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Constant" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Global" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Struct" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="FunctionPredecl" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Function" />
</file>
<class>
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Property" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Synthesize" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InitMethod" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="StaticMethod" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InstanceMethod" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="DeallocMethod" />
</class>
<extensions>
<pair source="cpp" header="h" fileNamingConvention="NONE" />
<pair source="c" header="h" fileNamingConvention="NONE" />
</extensions>
</Objective-C-extensions>
</code_scheme>
</component>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<option name="distributionType" value="DEFAULT_WRAPPED" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="modules">
<set>
<option value="$PROJECT_DIR$" />
<option value="$PROJECT_DIR$/app" />
</set>
</option>
<option name="resolveModulePerSourceSet" value="false" />
</GradleProjectSettings>
</option>
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="NullableNotNullManager">
<option name="myDefaultNullable" value="android.support.annotation.Nullable" />
<option name="myDefaultNotNull" value="android.support.annotation.NonNull" />
<option name="myNullables">
<value>
<list size="5">
<item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.Nullable" />
<item index="1" class="java.lang.String" itemvalue="javax.annotation.Nullable" />
<item index="2" class="java.lang.String" itemvalue="javax.annotation.CheckForNull" />
<item index="3" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.Nullable" />
<item index="4" class="java.lang.String" itemvalue="android.support.annotation.Nullable" />
</list>
</value>
</option>
<option name="myNotNulls">
<value>
<list size="4">
<item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.NotNull" />
<item index="1" class="java.lang.String" itemvalue="javax.annotation.Nonnull" />
<item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.NonNull" />
<item index="3" class="java.lang.String" itemvalue="android.support.annotation.NonNull" />
</list>
</value>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_7" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">
<option name="id" value="Android" />
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RunConfigurationProducerService">
<option name="ignoredProducers">
<set>
<option value="org.jetbrains.plugins.gradle.execution.test.runner.AllInPackageGradleConfigurationProducer" />
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestClassGradleConfigurationProducer" />
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestMethodGradleConfigurationProducer" />
</set>
</option>
</component>
</project>
\ No newline at end of file
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
android {
compileSdkVersion 27
defaultConfig {
applicationId "org.snowstar.wallpaper"
minSdkVersion 24
targetSdkVersion 27
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
implementation 'com.android.support:appcompat-v7:27.1.1'
implementation 'com.android.support.constraint:constraint-layout:1.0.2'
implementation 'com.android.support:design:27.1.1'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
package org.snowstar.wallpaper
import android.support.test.InstrumentationRegistry
import android.support.test.runner.AndroidJUnit4
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.Assert.*
/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getTargetContext()
assertEquals("org.snowstar.wallpaper", appContext.packageName)
}
}
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.snowstar.wallpaper">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<service
android:name=".MyWallpaperService"
android:label="@string/app_name"
android:permission="android.permission.BIND_WALLPAPER">
<intent-filter>
<action android:name="android.service.wallpaper.WallpaperService" />
</intent-filter>
<meta-data
android:name="android.service.wallpaper"
android:resource="@xml/wallpaper" />
</service>
<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:theme="@style/AppTheme.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
\ No newline at end of file
package org.snowstar.wallpaper
import android.app.Activity
import android.content.Intent
import android.graphics.BitmapFactory
import android.net.Uri
import android.os.Bundle
import android.provider.OpenableColumns
import android.support.v7.app.AppCompatActivity
import android.support.v7.widget.LinearLayoutManager
import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.android.synthetic.main.content_main.*
import java.io.File
import java.io.FileInputStream
import java.io.FileOutputStream
class MainActivity : AppCompatActivity() {
private val OPEN_FILE__CODE = 1
private lateinit var adapter: RecyclerAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setSupportActionBar(toolbar)
fab.setOnClickListener { _ ->
val intent = Intent(Intent.ACTION_GET_CONTENT)
intent.addCategory(Intent.CATEGORY_OPENABLE)
intent.type = "image/*"
startActivityForResult(intent, OPEN_FILE__CODE)
}
adapter = RecyclerAdapter(this)
list_view.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
list_view.adapter = adapter
loadPic()
}
/**
* 读取已经存在的图片显示
*/
private fun loadPic() {
val externalFilesDir = getExternalFilesDir("pics")
val pics = externalFilesDir.list()
for (pic in pics) {
val picPath = externalFilesDir.path + "/" + pic
val file = FileInputStream(File(picPath))
val bitmap = BitmapFactory.decodeStream(file)
adapter.add(bitmap, picPath)
}
}
private fun addPic(uri: Uri) {
// 复制图片到应用目录, 供后台服务使用
val cursor = contentResolver.query(uri, null, null, null, null)
if (cursor != null && cursor.moveToFirst()) {
val name = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME))
val externalFilesDir = getExternalFilesDir("pics")
val copyPath = externalFilesDir.canonicalPath + "/" + name
val outputStream = FileOutputStream(File(copyPath))
var inputStream = contentResolver.openInputStream(uri)
val tmp = ByteArray(inputStream.available())
inputStream.read(tmp)
outputStream.write(tmp)
// 加入显示
inputStream = contentResolver.openInputStream(uri)
val bitmap = BitmapFactory.decodeStream(inputStream)
adapter.add(bitmap, copyPath)
outputStream.close()
inputStream.close()
}
cursor.close()
val notifyIntent = Intent(this, MyWallpaperService::class.java)
notifyIntent.action = "org.snowstar.wallpaper.data_change"
startService(notifyIntent)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
when (requestCode) {
OPEN_FILE__CODE -> {
if (resultCode == Activity.RESULT_OK && data != null) {
val uri = data.data
addPic(uri)
}
}
}
}
}
package org.snowstar.wallpaper
import android.content.Context
import android.content.Intent
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Paint
import android.graphics.Rect
import android.os.Handler
import android.service.wallpaper.WallpaperService
import android.util.DisplayMetrics
import android.util.Log
import android.view.SurfaceHolder
import android.view.WindowManager
import java.io.File
import java.io.FileInputStream
import java.util.*
class MyWallpaperService() : WallpaperService() {
private val pics: MutableList<Bitmap> = mutableListOf()
private val rand = Random()
private val paint = Paint().apply {
isAntiAlias = true
isDither = true
// isFilterBitmap = true
}
private var dim = 0.0
private var deviceWidth = 0
private var deviceHeight = 0
override fun onCreateEngine(): Engine {
return MyEngine()
}
override fun onCreate() {
// 获取实际屏幕长宽
// 由于 desiredMinimumWidth 比实际宽度要多, 如果用 desiredMinimumWidth 会导致歪掉
val displayMetrics = DisplayMetrics()
val windowManager = getSystemService(Context.WINDOW_SERVICE) as WindowManager
windowManager.defaultDisplay.getRealMetrics(displayMetrics)
deviceHeight = displayMetrics.heightPixels
deviceWidth = displayMetrics.widthPixels
// 设备屏幕长宽比
dim = deviceHeight.toDouble() / deviceWidth
loadFile()
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
if (intent != null) {
if (intent.action == "org.snowstar.wallpaper.data_change") {
// 有新增图片, 刷新
loadFile()
}
}
return super.onStartCommand(intent, flags, startId)
}
/**
* 读取应用目录内所有图片
*/
private fun loadFile() {
val externalFilesDir = getExternalFilesDir("pics")
val files = externalFilesDir.list()
for (pic in files) {
val picPath = externalFilesDir.path + "/" + pic
val file = FileInputStream(File(picPath))
val bitmap = BitmapFactory.decodeStream(file)
val picDim: Double = bitmap.height.toDouble() / bitmap.width // 图像长宽比
val scale: Double = deviceHeight.toDouble() / bitmap.height // 缩放比
val scaledBitmap: Bitmap
scaledBitmap = if (picDim < dim) {
Bitmap.createScaledBitmap(bitmap, (bitmap.width * scale).toInt(), deviceHeight, true)
} else {
Bitmap.createScaledBitmap(bitmap, deviceWidth, (bitmap.height * scale).toInt(), true)
}
pics.add(scaledBitmap)
}
}
inner class MyEngine : WallpaperService.Engine() {
private val handler: Handler = Handler()
private var current = 0
private var xOffset = 0f
private lateinit var targetRect: Rect
private val task = Runnable {
if (!pics.isEmpty())
current = rand.nextInt(pics.size)
loop()
}
override fun onCreate(surfaceHolder: SurfaceHolder?) {
targetRect = Rect(0, 0, deviceWidth, deviceHeight)
desiredMinimumHeight
}
override fun onVisibilityChanged(visible: Boolean) {
if (visible) {
// 切换时立即刷新屏幕
handler.postDelayed(task, 100)
} else handler.removeCallbacks(task) // 不可见时取消定时器. 节省电量
}
override fun onOffsetsChanged(xOffset: Float, yOffset: Float, xOffsetStep: Float, yOffsetStep: Float, xPixelOffset: Int, yPixelOffset: Int) {
this.xOffset = xOffset
drawPicture()
}
/**
* 定时循环执行绘制.
*/
private fun loop() {
handler.removeCallbacks(task)
drawPicture()
handler.postDelayed(task, 5000)
}
/**
* 执行绘制图片
*/
private fun drawPicture() {
if (pics.isEmpty()) return
val c = surfaceHolder.lockCanvas()
val pic = pics[current]
Log.d("drawPicture", "pic ${pic.height}, ${pic.width}")
val picDim: Double = pic.height.toDouble() / pic.width // 图像长宽比
if (picDim < dim) {
// 对于横向长的图片, 首屏绘制中间部分, 横向拓展
/**
* ------------------------
* | | | |
* | pic |device| |
* | | | |
* | | | |
* ------------------------
* ^--------------^
* 计算这部分长度
*/
val tmpWidth = ((pic.width + deviceWidth) / 2).toInt()
/**
* ------------------------
* | | | |
* | pic |device| |
* | | | |
* | | | |
* ------------------------
* ^-------^
* 计算这部分长度
*/
val halfWidth = ((pic.width - deviceWidth) / 2).toInt()
// 根据当前所在屏计算绘制偏移量(左), 乘以 0.3 使滚动幅度减小
val start = ((tmpWidth - deviceWidth) * xOffset * 0.3).toInt() + halfWidth
val end = start + (deviceWidth).toInt() // 绘制偏移终点(右)
// 实际裁剪图片区域
val rect = Rect(start, 0, end, pic.height)
c.drawBitmap(pic, rect, targetRect, paint)
} else {
// 竖向长的图片, 绘制垂直方向中心部分.
val top = ((pic.height - deviceHeight) / 2).toInt()
val rect = Rect(0, top, pic.width, top + (deviceHeight).toInt())
c.drawBitmap(pic, rect, targetRect, paint)
}
surfaceHolder.unlockCanvasAndPost(c)
}
}
}
\ No newline at end of file
package org.snowstar.wallpaper
import android.app.AlertDialog
import android.app.Dialog
import android.content.Context
import android.content.DialogInterface
import android.content.Intent
import android.graphics.Bitmap
import android.support.v7.widget.RecyclerView
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import java.io.File